/**
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   This file is part of the LDP4j Project:
 *     http://www.ldp4j.org/
 *
 *   Center for Open Middleware
 *     http://www.centeropenmiddleware.com/
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   Copyright (C) 2014-2016 Center for Open Middleware.
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   Licensed 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.
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   Artifact    : org.ldp4j.commons:ldp4j-commons-core:0.2.2
 *   Bundle      : ldp4j-commons-core-0.2.2.jar
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 */
package org.ldp4j.commons.net;

import java.net.URI;

/**
 * Mutable URI decomposition into its standard components.
 * @see <a href="https://tools.ietf.org/html/rfc3986#section-5.2.2">RFC 3896:  Uniform Resource Identifier (URI): Generic Syntax - 5.2.2.  Transform References</>
 */
final class URIRef {

	String scheme;
	String authority;
	String path;
	String query;
	String fragment;

	private URIRef(String scheme, String authority, String path, String query, String fragment) {
		this.scheme = scheme;
		this.authority = authority;
		this.path = path;
		this.query = query;
		this.fragment = fragment;
	}

	/**
	 * 5.3.  Component Recomposition
	 *
	 *    Parsed URI components can be recomposed to obtain the corresponding
	 *    URI reference string.  Using pseudocode, this would be:
	 *
	 *       result = ""
	 *
	 *       if defined(scheme) then
	 *          append scheme to result;
	 *          append ":" to result;
	 *       endif;
	 *
	 *       if defined(authority) then
	 *          append "//" to result;
	 *          append authority to result;
	 *       endif;
	 *
	 *       append path to result;
	 *
	 *       if defined(query) then
	 *          append "?" to result;
	 *          append query to result;
	 *       endif;
	 *
	 *       if defined(fragment) then
	 *          append "#" to result;
	 *          append fragment to result;
	 *       endif;
	 *
	 *       return result;
	 *
	 *    Note that we are careful to preserve the distinction between a
	 *    component that is undefined, meaning that its separator was not
	 *    present in the reference, and a component that is empty, meaning that
	 *    the separator was present and was immediately followed by the next
	 *    component separator or the end of the reference.
	 */
	URI toURI() {
		StringBuilder builder=new StringBuilder();
		if(defined(this.scheme)) {
			builder.append(this.scheme);
			builder.append(":");
		}
		if(defined(this.authority)) {
			builder.append("//");
			builder.append(this.authority);
		}
		if(defined(this.path)) {
			builder.append(this.path);
		}
		if(defined(this.query)) {
			builder.append("?");
			builder.append(this.query);
		}
		if(defined(this.fragment)) {
			builder.append("#");
			builder.append(this.fragment);
		}
		String rawURI = builder.toString();
		return URI.create(rawURI);
	}

	static URIRef create(URI ref) {
		return new URIRef(ref.getScheme(),ref.getAuthority(),ref.getPath(),ref.getQuery(),ref.getFragment());
	}

	static URIRef create() {
		return new URIRef(null,null,"",null,null);
	}

	private static boolean defined(String value) {
		return value!=null && !value.isEmpty();
	}

}


/**
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   This file is part of the LDP4j Project:
 *     http://www.ldp4j.org/
 *
 *   Center for Open Middleware
 *     http://www.centeropenmiddleware.com/
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   Copyright (C) 2014-2016 Center for Open Middleware.
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   Licensed 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.
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   Artifact    : org.ldp4j.commons.rmf:rmf-bean:0.2.2
 *   Bundle      : rmf-bean-0.2.2.jar
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 */
package org.ldp4j.rdf.bean.spi;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.ReflectPermission;
import java.util.Properties;
import java.util.ServiceLoader;
import java.util.concurrent.atomic.AtomicReference;

import org.ldp4j.rdf.bean.JARBContext;
import org.ldp4j.rdf.bean.NamingPolicy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * RuntimeInstance is an abstract factory class that provides various methods for
 * the creation of objects that implement the ALM iStack LDP Kernel SPI (LKS for short).
 * These methods are designed for use by other LKA classes and are not intended
 * to be called directly by applications.
 * RuntimeInstance allows the standard LKA classes to use different LKS
 * implementations without any code changes.

 *

 *
 * An implementation of LKS MUST provide a concrete subclass of RuntimeInstance.
 * Using the supplied RuntimeInstance this can be provided to LKS in one of two
 * ways:

 *

 *
An instance of RuntimeInstance can be instantiated and injected using its
 * static method setInstance. In this case the implementation is responsible
 * for creating the instance; this option is intended for use with
 * implementations based on IoC frameworks.

 *
The class to be used can be configured, (see below). In this case CBLS is
 * responsible for instantiating an instance of the class and the configured
 * class MUST have a public constructor which takes no arguments.

 *

 *
 * Note that an implementation MAY supply an alternate implementation of the
 * RuntimeInstance API class (provided it passes the TCK signature test and
 * behaves according to the specification) that supports alternate means of
 * locating a concrete subclass.

 *

 *
 * A CBLS implementation may rely on a particular implementation of
 * RuntimeInstance being used – applications SHOULD NOT override the supplied
 * RuntimeInstance instance with an application-supplied alternative and doing so
 * may cause unexpected problems.
 *

 *
 */
public abstract class RuntimeInstance {

	private static final String INSTANTIATE_ACTION = "instantiate";

	private static final Logger LOGGER=LoggerFactory.getLogger(RuntimeInstance.class);

	public static final String RDF_BEAN_SPI_RUNTIMEINSTANCE_FINDER = "org.ldp4j.rdf.bean.spi.runtimeinstance.finder";

	/**
	 * Name of the configuration file where the
	 * {@link RuntimeInstance#RDF_BEAN_SPI_PROPERTY} property that
	 * identifies the {@link RuntimeInstance} implementation to be returned from
	 * {@link RuntimeInstance#getInstance()} can be defined.
	 */
	public static final String RDF_BEAN_CFG = "rdf-bean.properties";

	/**
	 * Name of the property identifying the {@link RuntimeInstance} implementation
	 * to be returned from {@link RuntimeInstance#getInstance()}.
	 */
	public static final String RDF_BEAN_SPI_PROPERTY = "org.ldp4j.rdf.bean.spi.RuntimeInstance";

	private static final AtomicReference<RuntimeInstance> CACHED_DELEGATE=new AtomicReference<RuntimeInstance>();

	private static ReflectPermission suppressAccessChecksPermission = new ReflectPermission("suppressAccessChecks");

	/**
	 * Allows custom implementations to extend the {@code RuntimeInstance} class.
	 */
	protected RuntimeInstance() {
	}

	/**
	 * Obtain a {@code RuntimeInstance} instance using the method described in
	 * {@link #getInstance}.
	 *
	 * @return an instance of {@code RuntimeInstance}.
	 */
	private static RuntimeInstance findDelegate() {
		try {
			RuntimeInstance result=createRuntimeInstanceFromSPI();
			if(result==null) {
				result=createRuntimeInstanceFromConfigurationFile();
			}

			if(result==null) {
				final String delegateClassName = System.getProperty(RDF_BEAN_SPI_PROPERTY);
				if(delegateClassName!=null) {
					result=createRuntimeInstanceForClassName(delegateClassName);
				}
			}

			if(result==null) {
				result=new DefaultRuntimeInstance();
			}

			return result;
		} catch (final Exception ex) {
			throw new IllegalStateException("Could not find runtime delegate",ex);
		}
	}

	private static RuntimeInstance createRuntimeInstanceFromConfigurationFile() {
		RuntimeInstance result=null;
		final File configFile = getConfigurationFile();
		if(configFile.canRead()) {
			InputStream is=null;
			try {
				is=new FileInputStream(configFile);
				final Properties configProperties=new Properties();
				configProperties.load(is);
				final String delegateClassName=configProperties.getProperty(RDF_BEAN_SPI_PROPERTY);
				if(delegateClassName!=null) {
					result=createRuntimeInstanceForClassName(delegateClassName);
				}
				if(delegateClassName==null && LOGGER.isWarnEnabled()) {
					LOGGER.warn("Configuration file '"+configFile.getAbsolutePath()+"' does not define a delegate class name");
				}
			} catch(final FileNotFoundException e) {
				if(LOGGER.isDebugEnabled()) {
					LOGGER.debug("Could not find runtime instance configuration file '"+configFile.getAbsolutePath()+"'",e);
				}
			} catch(final IOException e) {
				if(LOGGER.isWarnEnabled()) {
					LOGGER.warn("Could not load runtime instance configuration file '"+configFile.getAbsolutePath()+"'",e);
				}
			} finally {
				closeQuietly(is, "Could find close properties");
			}
		}
		return result;
	}

	/**
	 * Get the configuration file for the Runtime Instance: a file named
	 * {@link RuntimeInstance#RDF_BEAN_CFG} in the lib directory of
	 * current JAVA_HOME.
	 *
	 * @return The configuration file for the runtime instance.
	 */
	private static File getConfigurationFile() {
		return new File(new File(System.getProperty("java.home")),"lib"+File.separator+RDF_BEAN_CFG);
	}

	/**
	 * Close an input stream logging possible failures.
	 * @param is The input stream that is to be closed.
	 * @param message The message to log in case of failure.
	 */
	private static void closeQuietly(final InputStream is, final String message) {
		if(is!=null) {
		try {
			is.close();
		} catch (final Exception e) {
			if(LOGGER.isWarnEnabled()) {
				LOGGER.warn(message,e);
			}
		}
		}
	}

	private static RuntimeInstance createRuntimeInstanceFromSPI() {
		if(!"disable".equalsIgnoreCase(System.getProperty(RDF_BEAN_SPI_RUNTIMEINSTANCE_FINDER))) {
			for (final RuntimeInstance delegate : ServiceLoader.load(RuntimeInstance.class)) {
				return delegate;
			}
		}
		return null;
	}

	private static RuntimeInstance createRuntimeInstanceForClassName(final String delegateClassName) {
		RuntimeInstance result = null;
		try {
			final Class<?> delegateClass = Class.forName(delegateClassName);
			if(RuntimeInstance.class.isAssignableFrom(delegateClass)) {
				final Object impl = delegateClass.newInstance();
				result = RuntimeInstance.class.cast(impl);
			}
		} catch (final ClassNotFoundException e) {
			handleFailure(delegateClassName, "find", e);
		} catch (final InstantiationException e) {
			handleFailure(delegateClassName, INSTANTIATE_ACTION, e);
		} catch (final IllegalAccessException e) {
			handleFailure(delegateClassName, INSTANTIATE_ACTION, e);
		}
		return result;
	}

	/**
	 * @param delegateClassName
	 * @param action
	 * @param failure
	 */
	private static void handleFailure(final String delegateClassName, final String action,
			final Exception failure) {
		if(LOGGER.isWarnEnabled()) {
			LOGGER.warn("Could not "+action+" delegate class "+delegateClassName,failure);
		}
	}

	/**
	 * Obtain a {@code RuntimeInstance} instance. If an instance had not already
	 * been created and set via {@link #setInstance(RuntimeInstance)}, the first
	 * invocation will create an instance which will then be cached for future
	 * use.
	 *
	 *

	 * The algorithm used to locate the RuntimeInstance subclass to use consists
	 * of the following steps:
	 *

	 *

	 *

	 * If a resource with the name of
	 * {@code META-INF/services/org.centeropenmiddleware.almistack.poc.clients.spi.RuntimeInstance} exists, then
	 * its first line, if present, is used as the UTF-8 encoded name of the
	 * implementation class.

	 *

	 * If the $java.home/lib/poc-business-logic.properties file exists and it is readable by
	 * the {@code java.util.Properties.load(InputStream)} method and it contains
	 * an entry whose key is {@code org.centeropenmiddleware.almistack.poc.clients.spi.RuntimeInstance}, then the
	 * value of that entry is used as the name of the implementation class.

	 *

	 * If a system property with the name
	 * {@code org.centeropenmiddleware.almistack.poc.clients.spi.RuntimeInstance} is defined, then its value is
	 * used as the name of the implementation class.

	 *

	 * Finally, a default implementation class name is used.

	 *

	 *
	 * @return an instance of {@code RuntimeInstance}.
	 */
	public static RuntimeInstance getInstance() {
		RuntimeInstance result = RuntimeInstance.CACHED_DELEGATE.get();
		if (result != null) {
			return result;
		}
		synchronized(RuntimeInstance.CACHED_DELEGATE) {
			result=RuntimeInstance.CACHED_DELEGATE.get();
			if(result==null) {
				RuntimeInstance.CACHED_DELEGATE.set(findDelegate());
				result=RuntimeInstance.CACHED_DELEGATE.get();
			}
			return result;
		}
	}

	/**
	 * Set the runtime delegate that will be used by Client Business Logic API
	 * classes. If this method is not called prior to {@link #getInstance} then
	 * an implementation will be sought as described in {@link #getInstance}.
	 *
	 * @param delegate
	 *            the {@code RuntimeInstance} runtime delegate instance.
	 * @throws SecurityException
	 *             if there is a security manager and the permission
	 *             ReflectPermission("suppressAccessChecks") has not been
	 *             granted.
	 */
	public static void setInstance(final RuntimeInstance delegate) {
		final SecurityManager security = System.getSecurityManager();
		if (security != null) {
			security.checkPermission(suppressAccessChecksPermission);
		}
		RuntimeInstance.CACHED_DELEGATE.set(delegate);
	}

	public abstract JARBContext newContext(NamingPolicy policy);

	static class DefaultRuntimeInstance extends RuntimeInstance {

		private static final String ERROR_MESSAGE = String.format("No %s runtime instance found",RuntimeInstance.class);

		@Override
		public JARBContext newContext(final NamingPolicy policy) {
			throw new IllegalStateException(ERROR_MESSAGE);
		}

	}

}


/**
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   This file is part of the LDP4j Project:
 *     http://www.ldp4j.org/
 *
 *   Center for Open Middleware
 *     http://www.centeropenmiddleware.com/
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   Copyright (C) 2014-2016 Center for Open Middleware.
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   Licensed 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.
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   Artifact    : org.ldp4j.commons:ldp4j-commons-core:0.2.2
 *   Bundle      : ldp4j-commons-core-0.2.2.jar
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 */
package org.ldp4j.xml;

final class Characters {

	private Characters() {
	}

	static boolean isColon(final int codePoint) {
		return codePoint == ':';
	}

	/**
	 * Determines if a character is a Name start character.
	 *
	 * @param codePoint
	 *            The code point of the character to be tested. For UTF-16
	 *            characters the code point corresponds to the value of the char
	 *            that represents the character.
	 * @return {@code true} if {@code codePoint} is a Name start character,
	 *         otherwise {@code false}
	 * @see Name definition in the Extensible Markup
	 *      Language (XML) 1.0 (Fifth Edition) specification.
	 */
	static boolean isNameStartChar(final int codePoint) {
		return
			(codePoint == ':')                             ||
			(codePoint >= 'A' && codePoint <= 'Z')         ||
			(codePoint == '_')                             ||
			(codePoint >= 'a' && codePoint <= 'z')         ||
			(codePoint >= 0xC0 && codePoint <= 0xD6)       ||
			(codePoint >= 0xD8 && codePoint <= 0xF6)       ||
			(codePoint >= 0xF8 && codePoint <= 0x2FF)      ||
			(codePoint >= 0x370 && codePoint <= 0x37D)     ||
			(codePoint >= 0x37F && codePoint <= 0x1FFF)    ||
			(codePoint >= 0x200C && codePoint <= 0x200D)   ||
			(codePoint >= 0x2070 && codePoint <= 0x218F)   ||
			(codePoint >= 0x2C00 && codePoint <= 0x2FEF)   ||
			(codePoint >= 0x3001 && codePoint <= 0xD7FF)   ||
			(codePoint >= 0xF900 && codePoint <= 0xFDCF)   ||
			(codePoint >= 0xFDF0 && codePoint <= 0xFFFD)   ||
			(codePoint >= 0x10000 && codePoint <= 0xEFFFF);
	}

	/**
	 * Determines if a character is a Name character.
	 *
	 * @param codePoint
	 *        The code point of the character to be tested. For UTF-8 and UTF-16
	 *        characters the code point corresponds to the value of the char
	 *        that represents the character.
	 * @return {@code true} if {@code codePoint} is a Name character,
	 *         otherwise {@code false}
	 * @see Name definition in the Extensible Markup
	 *      Language (XML) 1.0 (Fifth Edition) specification.
	 */
	static boolean isNameChar(final int codePoint) {
		return
			isNameStartChar(codePoint)                   ||
			(codePoint == '-')                           ||
			(codePoint == '.')                           ||
			(codePoint >= '0' && codePoint <= '9')       ||
			(codePoint == 0xB7)                          ||
			(codePoint >= 0x0300 && codePoint <= 0x036F) ||
			(codePoint >= 0x203F && codePoint <= 0x2040);
	}

	/**
	 * Determines if a character is an NCName (Non-Colonised Name) start
	 * character.
	 *
	 * @param codePoint
	 *        The code point of the character to be tested. For UTF-8 and UTF-16
	 *        characters the code point corresponds to the value of the char
	 *        that represents the character.
	 * @return {@code true} if {@code codePoint} is a NCName start character,
	 *         otherwise {@code false}.
	 */
	static boolean isNCNameStartChar(final int codePoint) {
		return !isColon(codePoint) && isNameStartChar(codePoint);
	}

	/**
	 * Determines if a character is an NCName (Non-Colonised Name) character.
	 *
	 * @param codePoint
	 *        The code point of the character to be tested. For UTF-8 and UTF-16
	 *        characters the code point corresponds to the value of the char
	 *        that represents the character.
	 * @return {@code true} if {@code codePoint} is a NCName character,
	 *         otherwise {@code false}.
	 */
	static boolean isNCNameChar(final int codePoint) {
		return !isColon(codePoint) && isNameChar(codePoint);
	}

}


/**
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   This file is part of the LDP4j Project:
 *     http://www.ldp4j.org/
 *
 *   Center for Open Middleware
 *     http://www.centeropenmiddleware.com/
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   Copyright (C) 2014-2016 Center for Open Middleware.
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   Licensed 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.
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   Artifact    : org.ldp4j.commons:ldp4j-commons-http:0.2.2
 *   Bundle      : ldp4j-commons-http-0.2.2.jar
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 */
package org.ldp4j.http;

import static com.google.common.base.Preconditions.checkArgument;
import static java.util.Objects.requireNonNull;

import java.util.List;
import java.util.Locale;

import com.google.common.base.CharMatcher;
import com.google.common.collect.Lists;

/**
 * Utility class for handling Language instances.
 *
 * @see Language
 */
public final class Languages {

	private static final ImmutableLanguage WILDCARD_INSTANCE=new ImmutableLanguage(null);

	private static final class Requirements {

		private final String type;
		private final String composition;
		private final CharMatcher matcher;

		Requirements(final String type,final String composition,final CharMatcher matcher) {
			this.type = type;
			this.composition = composition;
			this.matcher = matcher;

		}

		private boolean hasValidLength(final String tag) {
			return tag.length()>0 && tag.length()<9;
		}

		String verify(final String tag) {
			checkArgument(
				this.matcher.matchesAllOf(tag),
				"Invalid language-range: %s must be %s ('%s')",
				this.type,
				this.composition,
				tag);
			checkArgument(
				hasValidLength(tag),
				"Invalid language-range: %s length must be greater than 1 and lower than 9 ('%s' --> %s)",
				this.type,
				tag,
				tag.length());
			return tag;
		}
	}

	private static final Requirements[] REQUIREMENTS={
		new Requirements("primary tag","alphabetical",CharMatcher.JAVA_LETTER),
		new Requirements("subtag","alphanumerical",CharMatcher.JAVA_LETTER_OR_DIGIT),
	};

	private static final String WILDCARD = "*";

	private Languages() {
	}

	/**
	 * Create a wildcard {@code Language}
	 *
	 * @return a wildcard {@code Language} instance
	 */
	public static Language wildcard() {
		return WILDCARD_INSTANCE;
	}

	/**
	 * Create a {@code Language} for a given {@code Locale}.
	 *
	 * @param locale
	 *            the {@code Locale} for the {@code Language}
	 * @return a {@code Language} instance with the specified {@code Locale}
	 * @throws NullPointerException
	 *             if {@code locale} is {@code null}
	 * @throws IllegalArgumentException
	 *             if the provided locale is a variant (i.e.,
	 *             {@link Locale#getVariant()} is not empty)
	 */
	public static Language of(final Locale locale) {
		requireNonNull(locale,"Locale cannot be null");
		checkArgument(locale.getVariant().isEmpty(),"Language does not allow locale variants (%s)",locale.getVariant());
		return new ImmutableLanguage(locale);
	}

	/**
	 * Create a {@code Language} instance from a valid {@code language-range}
	 * definition.
	 *
	 * @param language
	 *            the language-range for the language to be loaded.
	 * @return the {@code Language} instance for the specified language-range
	 * @throws NullPointerException
	 *             if {@code language} is null
	 * @throws IllegalArgumentException
	 *             if {@code language} is not a valid {@code language-range}
	 * @see RFC 4674 -
	 *      Section 2.1
	 * @see RFC 7231
	 *      - Section 5.3.5
	 */
	public static Language fromString(final String language) {
		requireNonNull(language,"Language cannot be null");
		if(WILDCARD.equals(language)) {
			return wildcard();
		} else {
			return of(parseLocale(language));
		}
	}

	/**
	 * Implements Basic Filtering matching algorithm defined in
	 * Section
	 * 3.3.1 of the Matching of Language Tags (BCP 47)
	 *
	 * @param language1
	 *            the including language
	 * @param language2
	 *            the language that will be checked for inclusion
	 * @return true, if the {@code language1} includes {@code language2}
	 *         attending to the aforementioned algorithm.
	 */
	public static boolean includes(final Language language1, final Language language2) {
		if(language1==null || language2==null) {
			return false;
		}
		if(language1.isWildcard()) {
			return true;
		}
		if(language2.isWildcard()) {
			return false;
		}
		return
			language1.primaryTag().equals(language2.primaryTag()) &&
			(language1.subTag().isEmpty() || language1.subTag().equals(language2.subTag()));
	}

	private static Locale parseLocale(final String locale) {
		final List<String> tags=getTags(locale);
		checkArgument(tags.size()<3,"Invalid language-range: to many tags (%s)",tags.size());
		return
			new Locale(
				verifyTag(tags,0),
				verifyTag(tags,1));
	}

	private static List<String> getTags(final String locale) {
		final List<String> tags=Lists.newArrayList();
		final int end = locale.length();
		int start=0;
		do {
			final int next=locale.indexOf('-',start);
			String tag=null;
			if(next<0) {
				tag=locale.substring(start,end);
				start=end;
			} else {
				tag=locale.substring(start,next);
				start=next+1;
			}
			tags.add(tag);
		} while(start<end);
		return tags;
	}

	private static String verifyTag(final List<String> parts, final int index) {
		if(parts.size()<=index) {
			return "";
		}
		final Requirements requirement=REQUIREMENTS[index];
		return
			requirement.
				verify(parts.get(index));
	}

}


/**
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   This file is part of the LDP4j Project:
 *     http://www.ldp4j.org/
 *
 *   Center for Open Middleware
 *     http://www.centeropenmiddleware.com/
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   Copyright (C) 2014-2016 Center for Open Middleware.
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   Licensed 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.
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   Artifact    : org.ldp4j.framework:ldp4j-application-kernel-jpa:0.2.2
 *   Bundle      : ldp4j-application-kernel-jpa-0.2.2.jar
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 */
package org.ldp4j.application.kernel.persistence.encoding;

import java.io.IOException;
import java.io.Serializable;

import org.ldp4j.application.data.Name;

import com.google.common.io.BaseEncoding;

abstract class AbstractEncoder extends Encoder {

	/**
	 * {@inheritDoc}
	 */
	@Override
	public final String encode(Name<?> name) {
		if(name==null) {
			return null;
		}
		try {
			Serializable target = prepare(name);
			byte[] serializedData=SerializationUtils.serialize(target);
			return BaseEncoding.base64().encode(serializedData);
		} catch (IOException e) {
			throw new AssertionError("Serialization should not fail",e);
		}
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public final <T extends Serializable> Name<T> decode(String data) {
		if(data==null) {
			return null;
		}
		try {
			byte[] serializedData=BaseEncoding.base64().decode(data);
			Serializable subject=SerializationUtils.deserialize(serializedData, Serializable.class);
			return assemble(subject);
		} catch (IOException e) {
			throw new AssertionError("Deserialization should not fail",e);
		}
	}

	protected abstract Serializable prepare(Name<?> name);

	protected abstract <T extends Serializable> Name<T> assemble(Serializable subject) throws IOException;

}


/**
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   This file is part of the LDP4j Project:
 *     http://www.ldp4j.org/
 *
 *   Center for Open Middleware
 *     http://www.centeropenmiddleware.com/
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   Copyright (C) 2014-2016 Center for Open Middleware.
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   Licensed 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.
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   Artifact    : org.ldp4j.framework:ldp4j-application-data:0.2.2
 *   Bundle      : ldp4j-application-data-0.2.2.jar
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 */
package org.ldp4j.application.data;

import java.io.Serializable;
import java.net.URI;

import org.ldp4j.application.vocabulary.Term;

import com.google.common.base.MoreObjects;

final class NullPropertyHelper implements PropertyHelper {

	private final URI propertyId;

	private final IndividualPropertyHelperImpl iph;

	NullPropertyHelper(URI propertyId) {
		this.propertyId = propertyId;
		this.iph = new IndividualPropertyHelperImpl(new NullIndividualHelper(), this);
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public <T> T firstValue(Class<? extends T> aClazz) {
		return null;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public IndividualHelper firstIndividual() {
		return new NullIndividualHelper();
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public <T extends Serializable, S extends Individual<T, S>> T firstIndividual(Class<? extends S> clazz) {
		return null;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public IndividualPropertyHelper withLiteral(Object rawValue) {
		return new IndividualPropertyHelperImpl(new NullIndividualHelper(), this);
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public IndividualPropertyHelper withIndividual(Name<?> id) {
		return this.iph;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public IndividualPropertyHelper withIndividual(Name<?> id, String managerId) {
		return this.iph;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public IndividualPropertyHelper withIndividual(Name<?> id, String managerId, URI path) {
		return this.iph;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public IndividualPropertyHelper withIndividual(Name<?> id, String managerId, String path) {
		return this.iph;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public IndividualPropertyHelper withIndividual(URI id) {
		return new IndividualPropertyHelperImpl(new NullIndividualHelper(), this);
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public IndividualPropertyHelper withIndividual(String id) {
		return new IndividualPropertyHelperImpl(new NullIndividualHelper(), this);
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public IndividualPropertyHelper withIndividual(Term id) {
		return new IndividualPropertyHelperImpl(new NullIndividualHelper(), this);
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public String toString() {
		return
			MoreObjects.
				toStringHelper(getClass()).
					add("propertyId", this.propertyId).
					toString();
	}

}


/**
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   This file is part of the LDP4j Project:
 *     http://www.ldp4j.org/
 *
 *   Center for Open Middleware
 *     http://www.centeropenmiddleware.com/
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   Copyright (C) 2014-2016 Center for Open Middleware.
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   Licensed 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.
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   Artifact    : org.ldp4j.commons.rmf:rmf-bean:0.2.2
 *   Bundle      : rmf-bean-0.2.2.jar
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 */
package org.ldp4j.rdf.bean.impl.model;

import java.net.URI;

import org.ldp4j.rdf.Resource;
import org.ldp4j.rdf.URIRef;

final class ModelUtils {

	private static final char COLON = ':';
	private static final char SLASH = '/';
	private static final char HASH = '#';

	enum Namespace {
		RDF("http://www.w3.org/1999/02/22-rdf-syntax-ns#",1),
		RDFS("http://www.w3.org/2000/01/rdf-schema#",2),
		OWL("http://www.w3.org/2002/07/owl#",3),
		XML_SCHEMA("http://www.w3.org/2001/XMLSchema#",4),
		UNKNOWN("UNKNOWN",5),
		;
		private final String id;
		private final int priority;

		Namespace(String id, int priority) {
			this.id = id;
			this.priority = priority;
		}

		static Namespace fromURI(URIRef uri) {
			for(Namespace namespace:values()) {
				if(uri.getIdentity().toString().startsWith(namespace.getId())) {
					return namespace;
				}
			}
			return UNKNOWN;
		}

		int compare(Namespace n) {
			return priority-n.priority;
		}

		String getId() {
			return id;
		}

	}

	private ModelUtils() {
	}

	/**
	 * correct split if local name has no ':' and URI contains no '#' or '/'
	 */
	private static boolean isCorrectColonNamespaceURISplit(String namespace, String localName, char lastNsChar) {
		return
			lastNsChar == COLON &&
			isValidColonLocalName(localName) &&
			isValidColonNamespace(namespace);
	}

	/**
	 * correct split if local name has no '/' and URI contains no '#'
	 */
	private static boolean isCorrectSlashNamespaceURISplit(String namespace, String localName, char lastNsChar) {
		return
			lastNsChar == SLASH &&
			isValidSlashLocalName(localName) &&
			isValidSlashNamespace(namespace);
	}

	/**
	 *  correct split if namespace has no other '#'
	 */
	private static boolean isCorrectHashNamespaceURISplit(String namespace, int nsLength, char lastNsChar) {
		return
			lastNsChar == HASH &&
			namespace.lastIndexOf(HASH, nsLength - 2) == -1;
	}

	private static boolean isValidSlashNamespace(String namespace) {
		return namespace.indexOf(HASH) == -1;
	}

	private static boolean isValidSlashLocalName(String localName) {
		return
			localName.indexOf(SLASH) == -1 &&
			localName.indexOf(HASH) == -1;
	}

	private static boolean isValidColonNamespace(String namespace) {
		return namespace.indexOf(HASH) == -1 && namespace.indexOf(SLASH) == -1;
	}

	private static boolean isValidColonLocalName(String localName) {
		return
			localName.indexOf(COLON) == -1 &&
			localName.indexOf(HASH) == -1 &&
			localName.indexOf(SLASH) == -1;
	}

	static int compare(URIRef u1, URIRef u2) {
		int res = Namespace.fromURI(u1).compare(Namespace.fromURI(u2));
		if(res!=0) {
			return res;
		}
		return u1.toString().compareTo(u2.toString());
	}

	/**
	 * Finds the index of the first local name character in an (non-relative)
	 * URI. This index is determined by the following the following steps:
	 *

	 *
Find the first occurrence of the '#' character,
	 *
If this fails, find the last occurrence of the '/' character,
	 *
If this fails, find the last occurrence of the ':' character.
	 *
Add <tt>1<tt> to the found index and return this value.
	 *

	 * Note that the third step should never fail as every legal (non-relative)
	 * URI contains at least one ':' character to separate the scheme from the
	 * rest of the URI. If this fails anyway, the method will throw an
	 * {@link IllegalArgumentException}.
	 *
	 * @param uri
	 *        A URI string.
	 * @return The index of the first local name character in the URI string.
	 *         Note that this index does not reference an actual character if the
	 *         algorithm determines that there is not local name. In that case,
	 *         the return index is equal to the length of the URI string.
	 * @throws IllegalArgumentException
	 *         If the supplied URI string doesn't contain any of the separator
	 *         characters. Every legal (non-relative) URI contains at least one
	 *         ':' character to separate the scheme from the rest of the URI.
	 */
	static int getLocalNameIndex(String uri) {
		int separatorIdx = uri.indexOf(HASH);

		if (separatorIdx < 0) {
			separatorIdx = uri.lastIndexOf(SLASH);
		}

		if (separatorIdx < 0) {
			separatorIdx = uri.lastIndexOf(COLON);
		}

		if (separatorIdx < 0) {
			throw new IllegalArgumentException("No separator character founds in URI: " + uri);
		}

		return separatorIdx + 1;
	}

	/**
	 * Checks whether the URI consisting of the specified namespace and local
	 * name has been split correctly according to the URI splitting rules
	 * specified in {@link URI}.
	 *
	 * @param namespace
	 *        The URI's namespace, must not be <tt>null</tt>.
	 * @param localName
	 *        The URI's local name, must not be <tt>null</tt>.
	 * @return <tt>true</tt> if the specified URI has been correctly split into a
	 *         namespace and local name, <tt>false</tt> otherwise.
	 * @see URI
	 * @see #getLocalNameIndex(String)
	 */
	static boolean isCorrectURISplit(String namespace, String localName) {
		assert namespace != null : "namespace must not be null";
		assert localName != null : "localName must not be null";

		boolean result=false;
		int nsLength = namespace.length();
		if(nsLength>0) {
			char lastNsChar=namespace.charAt(nsLength-1);
			result=
				isCorrectHashNamespaceURISplit(namespace, nsLength, lastNsChar) ||
				isCorrectSlashNamespaceURISplit(namespace, localName, lastNsChar) ||
				isCorrectColonNamespaceURISplit(namespace, localName, lastNsChar);
		}
		return result;
	}

	static String getNamespace(URIRef uri) {
		String uriString = uri.getIdentity().toString();
		int localNameIdx = getLocalNameIndex(uriString);
		return uriString.substring(0, localNameIdx);
	}

	static String getLocalName(URIRef uri) {
		String uriString = uri.getIdentity().toString();
		int localNameIdx = getLocalNameIndex(uriString);
		return uriString.substring(localNameIdx);
	}

	static String getIdentity(Resource<?> subject) {
		return subject.getIdentity().toString();
	}

}


/**
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   This file is part of the LDP4j Project:
 *     http://www.ldp4j.org/
 *
 *   Center for Open Middleware
 *     http://www.centeropenmiddleware.com/
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   Copyright (C) 2014-2016 Center for Open Middleware.
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   Licensed 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.
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   Artifact    : org.ldp4j.framework:ldp4j-application-engine-sdk:0.2.2
 *   Bundle      : ldp4j-application-engine-sdk-0.2.2.jar
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 */
package org.ldp4j.application.sdk;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;

import java.util.Collection;
import java.util.List;

import org.ldp4j.application.ext.Parameter;

import com.google.common.base.MoreObjects;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableList.Builder;
import com.google.common.collect.Lists;

/**
 * An immutable {@code Parameter} implementation.
 */
final class ImmutableQueryParameter implements Parameter {

	private static final long serialVersionUID = 5325850847736543804L;

	private final String name;
	private final ImmutableList<String> rawValues;

	private ImmutableQueryParameter(String name, ImmutableList<String> rawValues) {
		this.name=name;
		this.rawValues=rawValues;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public String name() {
		return this.name;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public boolean isMultivalued() {
		return this.rawValues.size()>1;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public int cardinality() {
		return this.rawValues.size();
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public String rawValue() {
		return this.rawValues.get(0);
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public List<String> rawValues() {
		return this.rawValues;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public <T> T rawValueAs(Class<? extends T> clazz) {
		return ObjectUtil.fromString(clazz, rawValue());
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public <T> List<T> rawValuesAs(Class<? extends T> clazz) {
		Builder<T> builder = ImmutableList.<T>builder();
		for(String rawValue:this.rawValues) {
			builder.add(ObjectUtil.fromString(clazz,rawValue));
		}
		return builder.build();
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public String toString() {
		return
			MoreObjects.
				toStringHelper(getClass()).
					add("name",this.name).
					add("rawValues",this.rawValues).
					toString();
	}

	static ImmutableQueryParameter create(String name, Collection<?> rawValues) {
		checkNotNull(name,"Parameter name cannot be null");
		checkArgument(!name.trim().isEmpty(),"Parameter name cannot be empty");
		checkNotNull(rawValues,"Raw values cannot be null");
		checkArgument(!rawValues.isEmpty(),"No raw values specified");
		Builder<String> strRawValues=ImmutableList.<String>builder();
		for(Object rawValue:rawValues) {
			strRawValues.add(rawValue.toString());
		}
		return new ImmutableQueryParameter(name, strRawValues.build());
	}

	static ImmutableQueryParameter create(String name, Object... rawValues) {
		return create(name, Lists.newArrayList(rawValues));
	}

}


/**
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   This file is part of the LDP4j Project:
 *     http://www.ldp4j.org/
 *
 *   Center for Open Middleware
 *     http://www.centeropenmiddleware.com/
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   Copyright (C) 2014-2016 Center for Open Middleware.
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   Licensed 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.
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   Artifact    : org.ldp4j.framework:ldp4j-application-kernel-api:0.2.2
 *   Bundle      : ldp4j-application-kernel-api-0.2.2.jar
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 */
package org.ldp4j.application.kernel.spi;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.ReflectPermission;
import java.util.Properties;
import java.util.ServiceLoader;
import java.util.concurrent.atomic.AtomicReference;

import org.ldp4j.application.kernel.constraints.ConstraintReportRepository;
import org.ldp4j.application.kernel.endpoint.EndpointRepository;
import org.ldp4j.application.kernel.lifecycle.LifecycleException;
import org.ldp4j.application.kernel.lifecycle.Managed;
import org.ldp4j.application.kernel.resource.ResourceRepository;
import org.ldp4j.application.kernel.transaction.TransactionManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public abstract class RuntimeDelegate implements Managed {

	private static final String INSTANTIATE_ACTION = "instantiate";

	private static final Logger LOGGER=LoggerFactory.getLogger(RuntimeDelegate.class);

	public static final String LDP4J_APPLICATION_SPI_RUNTIMEINSTANCE_FINDER = "org.ldp4j.application.spi.runtimeinstance.finder";

	/**
	 * Name of the configuration file where the
	 * {@link RuntimeDelegate#LDP4J_APPLICATION_SPI_PROPERTY} property that
	 * identifies the {@link RuntimeDelegate} implementation to be returned from
	 * {@link RuntimeDelegate#getInstance()} can be defined.
	 */
	public static final String LDP4J_APPLICATION_SPI_CFG = "ldp4j-application.properties";

	/**
	 * Name of the property identifying the {@link RuntimeDelegate} implementation
	 * to be returned from {@link RuntimeDelegate#getInstance()}.
	 */
	public static final String LDP4J_APPLICATION_SPI_PROPERTY = "org.ldp4j.application.spi.RuntimeInstance";

	private static final AtomicReference<RuntimeDelegate> CACHED_DELEGATE=new AtomicReference<RuntimeDelegate>();

	private static ReflectPermission suppressAccessChecksPermission = new ReflectPermission("suppressAccessChecks");

	/**
	 * Allows custom implementations to extend the {@code RuntimeInstance} class.
	 */
	protected RuntimeDelegate() {
	}

	/**
	 * Obtain a {@code RuntimeInstance} instance using the method described in
	 * {@link #getInstance}.
	 *
	 * @return an instance of {@code RuntimeInstance}.
	 */
	private static RuntimeDelegate findDelegate() {
		try {
			RuntimeDelegate result=createRuntimeInstanceFromSPI();
			if(result==null) {
				result=createRuntimeInstanceFromConfigurationFile();
			}

			if(result==null) {
				String delegateClassName = System.getProperty(LDP4J_APPLICATION_SPI_PROPERTY);
				if(delegateClassName!=null) {
					result=createRuntimeInstanceForClassName(delegateClassName);
				}
			}

			if(result==null) {
				result=new DefaultRuntimeInstance();
			}

			return result;
		} catch (Exception ex) {
			throw new IllegalStateException("Could not find runtime delegate",ex);
		}
	}

	private static RuntimeDelegate createRuntimeInstanceFromConfigurationFile() {
		RuntimeDelegate result=null;
		File configFile = getConfigurationFile();
		if(configFile.canRead()) {
			InputStream is=null;
			try {
				is=new FileInputStream(configFile);
				Properties configProperties=new Properties();
				configProperties.load(is);
				String delegateClassName=configProperties.getProperty(LDP4J_APPLICATION_SPI_PROPERTY);
				if(delegateClassName!=null) {
					result=createRuntimeInstanceForClassName(delegateClassName);
				}
				if(delegateClassName==null && LOGGER.isWarnEnabled()) {
					LOGGER.warn("Configuration file '"+configFile.getAbsolutePath()+"' does not define a delegate class name");
				}
			} catch(FileNotFoundException e) {
				if(LOGGER.isDebugEnabled()) {
					LOGGER.debug("Could not find runtime instance configuration file '"+configFile.getAbsolutePath()+"'",e);
				}
			} catch(IOException e) {
				if(LOGGER.isWarnEnabled()) {
					LOGGER.warn("Could not load runtime instance configuration file '"+configFile.getAbsolutePath()+"'",e);
				}
			} finally {
				closeQuietly(is, "Could not close configuration properties");
			}
		}
		return result;
	}

	/**
	 * Get the configuration file for the Runtime Instance: a file named
	 * {@link RuntimeDelegate#LDP4J_APPLICATION_SPI_CFG} in the lib directory of
	 * current JAVA_HOME.
	 *
	 * @return The configuration file for the runtime instance.
	 */
	private static File getConfigurationFile() {
		return new File(new File(System.getProperty("java.home")),"lib"+File.separator+LDP4J_APPLICATION_SPI_CFG);
	}

	/**
	 * Close an input stream logging possible failures.
	 * @param is The input stream that is to be closed.
	 * @param message The message to log in case of failure.
	 */
	private static void closeQuietly(InputStream is, String message) {
		if(is!=null) {
		try {
			is.close();
		} catch (Exception e) {
			if(LOGGER.isWarnEnabled()) {
				LOGGER.warn(message,e);
			}
		}
		}
	}

	private static RuntimeDelegate createRuntimeInstanceFromSPI() {
		if(!"disable".equalsIgnoreCase(System.getProperty(LDP4J_APPLICATION_SPI_RUNTIMEINSTANCE_FINDER))) {
			for (RuntimeDelegate delegate : ServiceLoader.load(RuntimeDelegate.class)) {
				return delegate;
			}
		}
		return null;
	}

	private static RuntimeDelegate createRuntimeInstanceForClassName(String delegateClassName) {
		RuntimeDelegate result = null;
		try {
			Class<?> delegateClass = Class.forName(delegateClassName);
			if(RuntimeDelegate.class.isAssignableFrom(delegateClass)) {
				Object impl = delegateClass.newInstance();
				result = RuntimeDelegate.class.cast(impl);
			}
		} catch (ClassNotFoundException e) {
			handleFailure(delegateClassName, "find", e);
		} catch (InstantiationException e) {
			handleFailure(delegateClassName, INSTANTIATE_ACTION, e);
		} catch (IllegalAccessException e) {
			handleFailure(delegateClassName, INSTANTIATE_ACTION, e);
		}
		return result;
	}

	/**
	 * @param delegateClassName
	 * @param action
	 * @param failure
	 */
	private static void handleFailure(String delegateClassName, String action, Exception failure) {
		if(LOGGER.isWarnEnabled()) {
			LOGGER.warn("Could not "+action+" delegate class "+delegateClassName,failure);
		}
	}

	/**
	 * Obtain a {@code RuntimeInstance} instance. If an instance had not already
	 * been created and set via {@link #setInstance(RuntimeDelegate)}, the first
	 * invocation will create an instance which will then be cached for future
	 * use.
	 *
	 *

	 * The algorithm used to locate the RuntimeInstance subclass to use consists
	 * of the following steps:
	 *

	 *

	 *

	 * If a resource with the name of
	 * {@code META-INF/services/org.centeropenmiddleware.almistack.poc.clients.spi.RuntimeInstance} exists, then
	 * its first line, if present, is used as the UTF-8 encoded name of the
	 * implementation class.

	 *

	 * If the $java.home/lib/poc-business-logic.properties file exists and it is readable by
	 * the {@code java.util.Properties.load(InputStream)} method and it contains
	 * an entry whose key is {@code org.centeropenmiddleware.almistack.poc.clients.spi.RuntimeInstance}, then the
	 * value of that entry is used as the name of the implementation class.

	 *

	 * If a system property with the name
	 * {@code org.centeropenmiddleware.almistack.poc.clients.spi.RuntimeInstance} is defined, then its value is
	 * used as the name of the implementation class.

	 *

	 * Finally, a default implementation class name is used.

	 *

	 *
	 * @return an instance of {@code RuntimeInstance}.
	 */
	public static RuntimeDelegate getInstance() {
		RuntimeDelegate result = RuntimeDelegate.CACHED_DELEGATE.get();
		if (result != null) {
			return result;
		}
		synchronized(RuntimeDelegate.CACHED_DELEGATE) {
			result=RuntimeDelegate.CACHED_DELEGATE.get();
			if(result==null) {
				RuntimeDelegate delegate = findDelegate();
				RuntimeDelegate.CACHED_DELEGATE.set(delegate);
				result=RuntimeDelegate.CACHED_DELEGATE.get();
			}
			return result;
		}
	}

	/**
	 * Set the runtime delegate that will be used by Client Business Logic API
	 * classes. If this method is not called prior to {@link #getInstance} then
	 * an implementation will be sought as described in {@link #getInstance}.
	 *
	 * @param delegate
	 *            the {@code RuntimeInstance} runtime delegate instance.
	 * @throws SecurityException
	 *             if there is a security manager and the permission
	 *             ReflectPermission("suppressAccessChecks") has not been
	 *             granted.
	 */
	public static void setInstance(final RuntimeDelegate delegate) {
		SecurityManager security = System.getSecurityManager();
		if (security != null) {
			security.checkPermission(suppressAccessChecksPermission);
		}
		RuntimeDelegate.CACHED_DELEGATE.set(delegate);
	}

	private static class DefaultRuntimeInstance extends RuntimeDelegate {

		private static final String ERROR_MESSAGE = String.format("No implementation for class '%s' could be found",RuntimeDelegate.class);

		@Override
		public ModelFactory getModelFactory() {
			throw new AssertionError(ERROR_MESSAGE);
		}

		@Override
		public ConstraintReportRepository getConstraintReportRepository() {
			throw new AssertionError(ERROR_MESSAGE);
		}

		@Override
		public EndpointRepository getEndpointRepository() {
			throw new AssertionError(ERROR_MESSAGE);
		}

		@Override
		public ResourceRepository getResourceRepository() {
			throw new AssertionError(ERROR_MESSAGE);
		}

		@Override
		public TransactionManager getTransactionManager() {
			throw new AssertionError(ERROR_MESSAGE);
		}

		@Override
		public void init() throws LifecycleException {
			throw new AssertionError(ERROR_MESSAGE);
		}

		@Override
		public void shutdown() throws LifecycleException {
			throw new AssertionError(ERROR_MESSAGE);
		}

	}

	public abstract ModelFactory getModelFactory();

	public abstract TransactionManager getTransactionManager();

	public abstract ResourceRepository getResourceRepository();

	public abstract EndpointRepository getEndpointRepository();

	public abstract ConstraintReportRepository getConstraintReportRepository();

}


/**
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   This file is part of the LDP4j Project:
 *     http://www.ldp4j.org/
 *
 *   Center for Open Middleware
 *     http://www.centeropenmiddleware.com/
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   Copyright (C) 2014-2016 Center for Open Middleware.
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   Licensed 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.
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   Artifact    : org.ldp4j.framework:ldp4j-application-api:0.2.2
 *   Bundle      : ldp4j-application-api-0.2.2.jar
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 */
package org.ldp4j.application;

import java.lang.ref.ReferenceQueue;
import java.util.Map;

import org.ldp4j.application.session.WriteSession;
import org.ldp4j.application.spi.RuntimeDelegate;
import org.ldp4j.application.spi.ShutdownListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.base.MoreObjects;
import com.google.common.collect.Maps;

/**
 * A proxy class that acts as a facade for interacting with the Application
 * Engine proactively.
 */
public final class ApplicationContext {

	private final class CleanerContextWriteSessionStateListener implements ContextWriteSessionStateListener {
		@Override
		public void onDispose(ContextWriteSessionState state) {
			untrackSession(state);
		}
	}

	private static final class ApplicationEngineSingleton {

		private static final ApplicationContext SINGLETON=new ApplicationContext();

		private ApplicationEngineSingleton() {
		}

	}

	private static final Logger LOGGER=LoggerFactory.getLogger(ApplicationContext.class);

	private final RuntimeDelegate delegate;
	private final Map<Long,ContextWriteSessionReference> references;
	private final Map<Long,Long> sessionOwner;
	private final Map<Long,Long> threadSession;
	private final ReferenceQueue<ContextWriteSession> referenceQueue;

	private ApplicationContext() {
		this.delegate=RuntimeDelegate.getInstance();
		this.references=Maps.newLinkedHashMap();
		this.sessionOwner=Maps.newLinkedHashMap();
		this.threadSession=Maps.newLinkedHashMap();
		this.referenceQueue=new ReferenceQueue<ContextWriteSession>();
		LOGGER.info("Initialized Application Context");
	}

	private ApplicationContextException failure(String fmt, Object... args) {
		String message=String.format(fmt,args);
		LOGGER.error(message);
		return new ApplicationContextException(message);
	}

	private void setUpWriteSessionCleaner() {
		if(WriteSessionCleaner.isActive()) {
			return;
		}
		WriteSessionCleaner.launch(this.referenceQueue);
		this.delegate.registerShutdownListener(
			new ShutdownListener(){
				@Override
				public void engineShutdown() {
					WriteSessionCleaner.terminate();
				}
			}
		);
	}

	private ContextWriteSession trackSession(ContextWriteSessionState state) {
		ContextWriteSession leakedSession =
			new ContextWriteSession(state);
		ContextWriteSessionReference reference =
			new ContextWriteSessionReference(
				leakedSession,
				state,
				this.referenceQueue);
		this.references.put(state.id(),reference);
		this.sessionOwner.put(state.id(),Thread.currentThread().getId());
		this.threadSession.put(Thread.currentThread().getId(),state.id());
		return leakedSession;
	}

	private synchronized void untrackSession(ContextWriteSessionState session) {
		long sessionId=session.id();
		long ownerId=this.sessionOwner.get(sessionId);
		this.references.remove(sessionId);
		this.sessionOwner.remove(sessionId);
		this.threadSession.remove(ownerId);
	}

	/**
	 * Create a {@code WriteSession}. Only one write session can be active per
	 * thread. Sessions should not be shared among threads.
	 *
	 * @return the write session
	 * @throws ApplicationContextException
	 *             if no write session can be created for whichever reason,
	 *             e.g., the Application Engine is off-line or is not available.
	 */
	public synchronized WriteSession createSession() throws ApplicationContextException {
		if(this.threadSession.containsKey(Thread.currentThread().getId())) {
			throw failure("Thread already has an active session");
		}

		if(this.delegate.isOffline()) {
			throw failure("The Application Engine is off-line");
		}

		WriteSession nativeSession=this.delegate.createSession();
		if(nativeSession==null) {
			throw failure("Could not create native write session");
		}

		setUpWriteSessionCleaner();

		return
			trackSession(
				new ContextWriteSessionState(
					nativeSession,
					new CleanerContextWriteSessionStateListener()));
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public synchronized String toString() {
		return
			MoreObjects.
				toStringHelper(getClass()).
					omitNullValues().
					add("delegate",this.delegate).
					add("references",this.references).
					add("sessionOwner",this.sessionOwner).
					add("threadSession",this.threadSession).
					toString();
	}

	/**
	 * Get the Application context.
	 *
	 * @return the application context.
	 */
	public static ApplicationContext getInstance() {
		return ApplicationEngineSingleton.SINGLETON;
	}

}


/**
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   This file is part of the LDP4j Project:
 *     http://www.ldp4j.org/
 *
 *   Center for Open Middleware
 *     http://www.centeropenmiddleware.com/
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   Copyright (C) 2014-2016 Center for Open Middleware.
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   Licensed 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.
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   Artifact    : org.ldp4j.commons:ldp4j-commons-http:0.2.2
 *   Bundle      : ldp4j-commons-http-0.2.2.jar
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 */
package org.ldp4j.http;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;

import java.util.BitSet;

import com.google.common.base.Strings;

final class HttpUtils {

	private static final char   PARAM_DELIMITER = ';';
	private static final char   DQUOTE          = '\"';

	private static final BitSet TOKEN;

	static {
		// ASCII Control codes
		final BitSet ctl = new BitSet(128);
		ctl.set(0x00,0x20);
		ctl.set(0x7F);

		// SP, DQUOTE and "(),/:;<=>?@[\]{}"
		final BitSet delimiters = new BitSet(128);
		delimiters.set(' ');
		delimiters.set('\"');
		delimiters.set('(');
		delimiters.set(')');
		delimiters.set(',');
		delimiters.set('/');
		delimiters.set(':');
		delimiters.set(';');
		delimiters.set('<');
		delimiters.set('=');
		delimiters.set('>');
		delimiters.set('?');
		delimiters.set('@');
		delimiters.set('[');
		delimiters.set('\\');
		delimiters.set(']');
		delimiters.set('{');
		delimiters.set('}');

		TOKEN = new BitSet(0x80);
		TOKEN.set(0,0x80);
		TOKEN.andNot(ctl);
		TOKEN.andNot(delimiters);
	}

	private HttpUtils() {
	}

	static String trimWhitespace(final String token) {
		checkNotNull(token,"Token cannot be null");
		int startOffset=0;
		int endOffset=token.length();
		while(startOffset<endOffset) {
			final char lastChar=token.charAt(startOffset);
			if(!HttpUtils.isWhitespace(lastChar)) {
				break;
			}
			startOffset++;
		}
		while(endOffset>startOffset) {
			final char lastChar=token.charAt(endOffset-1);
			if(!HttpUtils.isWhitespace(lastChar)) {
				break;
			}
			endOffset--;
		}
		return token.substring(startOffset,endOffset);
	}

	static boolean isQuotedString(final String str) {
		boolean result=false;
		final int length=str.length();
		if(length>1) {
			result=
				str.charAt(0)       ==DQUOTE &&
				str.charAt(length-1)==DQUOTE;
		}
		return result;
	}

	static String unquote(final String str) {
		return
			isQuotedString(str) ?
				str.substring(1, str.length() - 1) :
				str;
	}

	static boolean isWhitespace(final char ch) {
		return ch==' ' || ch=='\t';
	}

	static boolean isParameterDelimiter(final char ch) {
		return ch==PARAM_DELIMITER;
	}

	/**
	 * Checks the given type name for illegal characters, as defined in RFC
	 * 7230, section 3.2.6.
	 *
	 * @param token
	 *            the string to validate
	 * @throws IllegalArgumentException
	 *             in case of illegal characters
	 * @see Hypertext
	 *      Transfer Protocol (HTTP/1.1): Message Syntax and Routing, Section
	 *      3.2.6
	 */
	static void checkToken(final String token, final String message, Object... args) {
		checkNotNull(message,"Message cannot be null");
		try {
			validateLength(token);
			validateCharacters(token);
		} catch (IllegalArgumentException e) {
			throw new InvalidTokenException(String.format(message,args),token,e);
		}
	}

	static void checkToken(final String token) {
		checkToken(token,"Invalid token");
	}

	static Double checkQuality(final Double quality, final String parameter) {
		if(quality==null) {
			return null;
		}
		checkQualityRange(quality, parameter);
		checkArgument(DoubleUtils.hasPrecision(quality,3),"%s cannot have more than 3 decimals (%s)",parameter,quality);
		return quality;
	}

	static void checkQualityRange(final Double quality, final String parameter) {
		checkArgument(quality>=0.0D,"%s cannot be negative (%s)",parameter,quality);
		checkArgument(quality<=1.0D,"%s cannot be greater than 1 (%s)",parameter,quality);
	}

	private static void validateCharacters(final String token) {
		for(int i=0;i<token.length();i++) {
			final char ch=token.charAt(i);
			checkArgument(TOKEN.get(ch),"Invalid character '%s' in token '%s' at %s",ch,token,i);
		}
	}

	private static void validateLength(final String token) {
		checkArgument(!Strings.isNullOrEmpty(token),"Token cannot be empty");
	}

}


/**
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   This file is part of the LDP4j Project:
 *     http://www.ldp4j.org/
 *
 *   Center for Open Middleware
 *     http://www.centeropenmiddleware.com/
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   Copyright (C) 2014-2016 Center for Open Middleware.
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   Licensed 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.
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   Artifact    : org.ldp4j.commons:ldp4j-commons-core:0.2.2
 *   Bundle      : ldp4j-commons-core-0.2.2.jar
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 */
package org.ldp4j.commons;

public final class Reflection {

	private Reflection() {
	}

	/**
	 * Ensures that the given classes are initialized, as described in  JLS Section 12.4.2.
	 *
	 *

	 * WARNING: Normally it's a smell if a class needs to be explicitly
	 * initialized, because static state hurts system maintainability and
	 * testability. In cases when you have no choice while inter-operating with
	 * a legacy framework, this method helps to keep the code less ugly.
	 *
	 * @param classes the classes to be initialized
	 * @throws ExceptionInInitializerError
	 *             if an exception is thrown during initialization of a class
	 * @throws AssertionError
	 *             if the classloader fails to find the already loaded class
	 */
	public static void initialize(final Class<?>... classes) {
		for(final Class<?> clazz:classes) {
			try {
				Class.forName(clazz.getName(),true,clazz.getClassLoader());
			} catch (final ClassNotFoundException e) {
				throw new IllegalStateException("The class is already loaded, therefore it should be found",e);
			}
		}
	}

	/**
	 * TODO: Relax this constraint, it should be possible to do it from
	 * named-inner classes.
	 *
	 * @param scopeClass
	 *            the parent class from which to retrieve the subclass in the
	 *            scope.
	 *
	 * @return the subclass of the specified that is within the scope if found,
	 *         {@code null} otherwise.
	 */
	public static Class<?> getSubclassInScope(final Class<?> scopeClass) {
		final String callerClass = Thread.currentThread().getStackTrace()[3].getClassName();
		try {
			final Class<?> candidateClass = Class.forName(callerClass, false, Thread.currentThread().getContextClassLoader());
			if(!scopeClass.isAssignableFrom(candidateClass)) {
				return null;
			}
			return candidateClass;
		} catch (final ClassNotFoundException e) {
			throw new IllegalStateException("The class should always exist",e);
		}
	}

	/**
	 * Gets the caller class name.
	 *
	 * @return the caller class name
	 */
	public static String getCallerClassName() {
		return Thread.currentThread().getStackTrace()[3].getClassName();
	}

}


/**
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   This file is part of the LDP4j Project:
 *     http://www.ldp4j.org/
 *
 *   Center for Open Middleware
 *     http://www.centeropenmiddleware.com/
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   Copyright (C) 2014-2016 Center for Open Middleware.
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   Licensed 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.
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   Artifact    : org.ldp4j.framework:ldp4j-server-core:0.2.2
 *   Bundle      : ldp4j-server-core-0.2.2.jar
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 */
package org.ldp4j.server.data;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Iterator;
import java.util.Set;

import javax.ws.rs.core.MediaType;

import org.ldp4j.application.data.DataSet;
import org.ldp4j.application.data.DataSets;
import org.ldp4j.application.data.Individual;
import org.ldp4j.application.data.NamingScheme;
import org.ldp4j.application.vocabulary.LDP;
import org.ldp4j.application.vocabulary.RDF;
import org.ldp4j.application.vocabulary.RDFS;
import org.ldp4j.rdf.Namespaces;
import org.ldp4j.rdf.Triple;
import org.ldp4j.server.data.MediaTypeSupport.Marshaller;
import org.ldp4j.server.data.MediaTypeSupport.Unmarshaller;
import org.ldp4j.server.data.spi.ContentTransformationException;
import org.ldp4j.server.data.spi.Context;
import org.ldp4j.server.data.spi.RuntimeDelegate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.collect.Iterables;

// TODO: Add support for encodings
// TODO: Add support for charsets
// TODO: Add support for language
public final class DataTransformator {

	private static final String MEDIA_TYPE_CANNOT_BE_NULL = "Media type cannot be null";

	private static final Logger LOGGER=LoggerFactory.getLogger(DataTransformator.class);

	private static final ResourceResolver DEFAULT_RESOLVER = new NullResourceResolver();

	private static final URI DEFAULT_ENDPOINT=URI.create("");

	private URI endpoint;
	private URI applicationBase;

	private ResourceResolver resourceResolver;

	private boolean permanent;

	private MediaType mediaType;

	private Namespaces namespaces;

	private DataTransformator(DataTransformator dataTransformation) {
		setApplicationBase(dataTransformation.applicationBase);
		setEndpoint(dataTransformation.endpoint, dataTransformation.permanent);
		setResourceResolver(dataTransformation.resourceResolver);
		setMediaType(dataTransformation.mediaType);
		setNamespaces(dataTransformation.namespaces);
	}

	private DataTransformator() {
		setResourceResolver(DEFAULT_RESOLVER);
		setEndpoint(DEFAULT_ENDPOINT, true);
		setNamespaces(defaultNamespaces());
	}

	private Namespaces defaultNamespaces() {
		return
			new Namespaces().
				addPrefix("rdf", RDF.NAMESPACE).
				addPrefix("rdfs", RDFS.NAMESPACE).
				addPrefix("xsd", "http://www.w3.org/2001/XMLSchema#").
				addPrefix("ldp", LDP.NAMESPACE);
	}

	private void setMediaType(MediaType mediaType) {
		if(!isSupported(mediaType)) {
			throw new UnsupportedMediaTypeException("Unsupported media type '"+mediaType+"'",mediaType);
		}
		this.mediaType=mediaType;
	}

	private void setResourceResolver(ResourceResolver resolver) {
		this.resourceResolver = resolver;
	}

	private void setEndpoint(URI endpoint, boolean permanent) {
		this.endpoint = endpoint;
		this.permanent = permanent;
	}

	private void setApplicationBase(URI applicationBase) {
		this.applicationBase = applicationBase;
	}

	private void setNamespaces(Namespaces namespaces) {
		this.namespaces=new Namespaces(namespaces);
	}

	private URI baseEndpoint() {
		return this.applicationBase.resolve(this.endpoint);
	}

	private URI createAlternative(URI endpoint) {
		try {
			return
				new URI(
					endpoint.getScheme(),
					endpoint.getUserInfo(),
					"ldp4j".concat(endpoint.getHost()),
					endpoint.getPort(),
					endpoint.getPath(),
					endpoint.getFragment(),
					endpoint.getQuery()
				);
		} catch (URISyntaxException e) {
			throw new IllegalStateException("Alternative URI creation failed",e);
		}
	}

	private DataSet surrogateUnmarshall(String entity, URI endpoint) throws ContentTransformationException {
		TripleResolver tripleResolver=
			TripleResolver.
				builder().
					withApplication(this.applicationBase).
					withEndpoint(endpoint).
					withAlternative(createAlternative(endpoint)).
					withEntity(entity, this.mediaType).
					build();

		DataSet dataSet=DataSets.createDataSet(NamingScheme.getDefault().name(endpoint));
		ValueAdapter adapter=new ValueAdapter(resourceResolver,dataSet);
		for(TripleResolution tripleResolution:tripleResolver.tripleResolutions()) {
			Triple triple=tripleResolution.triple();
			Individual<?,?> individual=adapter.getIndividual(triple.getSubject(),tripleResolution.subjectResolution());
			individual.
				addValue(
					triple.getPredicate().getIdentity(),
					adapter.getValue(triple.getObject(),tripleResolution.objectResolution()));
		}
		return dataSet;
	}

	private DataSet permanentUnmarshall(String entity, URI endpoint) throws ContentTransformationException {
		Context context =
			ImmutableContext.
				newInstance(endpoint).
					setNamespaces(this.namespaces);

		Unmarshaller unmarshaller=MediaTypeSupport.newUnmarshaller(mediaType);
		return unmarshaller.unmarshall(context,this.resourceResolver,entity);
	}

	public DataTransformator permanentEndpoint(URI endpoint) {
		checkNotNull(endpoint,"Endpoint URI cannot be null");
		checkArgument(!endpoint.isAbsolute(),"Endpoint URI must be relative");
		DataTransformator result = new DataTransformator(this);
		result.setEndpoint(endpoint, true);
		return result;
	}

	public DataTransformator surrogateEndpoint(URI endpoint) {
		checkNotNull(endpoint,"Endpoint URI cannot be null");
		checkArgument(!endpoint.isAbsolute(),"Endpoint URI must be relative");
		DataTransformator result=new DataTransformator(this);
		result.setEndpoint(endpoint, false);
		return result;
	}

	/**
	 * Create transformator with the specified media type
	 *
	 * @param mediaType
	 *            the new media type
	 * @return a new transformator with the same configuration except for the
	 *         media type, which is set to the specified one
	 * @throws UnsupportedMediaTypeException
	 *             if the media type is not valid
	 */
	public DataTransformator mediaType(MediaType mediaType) {
		checkNotNull(mediaType,MEDIA_TYPE_CANNOT_BE_NULL);
		DataTransformator result = new DataTransformator(this);
		result.setMediaType(mediaType);
		return result;
	}

	public DataTransformator namespaces(Namespaces namespaces) {
		checkNotNull(namespaces,"Namespaces cannot be null");
		DataTransformator result = new DataTransformator(this);
		result.setNamespaces(namespaces);
		return result;
	}

	public DataTransformator enableResolution(ResourceResolver resourceResolver) {
		checkNotNull(endpoint,"Resource resolver cannot be null");
		DataTransformator result = new DataTransformator(this);
		result.setResourceResolver(resourceResolver);
		return result;
	}

	public DataTransformator disableResolution() {
		DataTransformator result = new DataTransformator(this);
		result.setResourceResolver(DEFAULT_RESOLVER);
		return result;
	}

	public DataSet unmarshall(String entity) throws IOException {
		checkNotNull(entity,"Entity cannot be null");
		checkNotNull(mediaType,MEDIA_TYPE_CANNOT_BE_NULL);
		LOGGER.trace("Raw entity to unmarshall: \n{}",entity);
		LOGGER.trace("Unmarshalling using base '{}'...",baseEndpoint());
		try {
			DataSet result=null;
			if(this.permanent) {
				result=permanentUnmarshall(entity,baseEndpoint());
			} else {
				result=surrogateUnmarshall(entity,baseEndpoint());
			}
			LOGGER.trace("Unmarshalled data set: \n{}",result);
			return result;
		} catch (ContentTransformationException e) {
			throw new IOException("Entity cannot be parsed as '"+mediaType+"'",e);
		}
	}

	public String marshall(DataSet representation) throws IOException {
		checkNotNull(representation,"Representation cannot be null");

		Context context =
			ImmutableContext.
				newInstance(baseEndpoint()).
					setNamespaces(this.namespaces);

		Marshaller marshaller=MediaTypeSupport.newMarshaller(mediaType);
		try {
			LOGGER.trace("Marshalling using base '{}'",context.getBase());
			String rawEntity = marshaller.marshall(context,this.resourceResolver,representation);
			LOGGER.trace("Marshalled entity: \n{}",rawEntity);
			return rawEntity;
		} catch (ContentTransformationException e) {
			throw new IOException("Resource representation cannot be serialized as '"+mediaType+"' ",e);
		}
	}

	public static Set<MediaType> supportedMediaTypes() {
		return RuntimeDelegate.getInstance().getSupportedMediaTypes();
	}

	public static boolean isSupported(MediaType mediaType) {
		checkNotNull(mediaType,MEDIA_TYPE_CANNOT_BE_NULL);
		Set<MediaType> supportedMediaTypes = supportedMediaTypes();
		checkState(supportedMediaTypes!=null,"Supported media types cannot be null");
		boolean supported=false;
		for(Iterator<MediaType> it=supportedMediaTypes.iterator();it.hasNext() && !supported;) {
			supported=it.next().isCompatible(mediaType);
		}
		return supported;
	}

	public static DataTransformator create(URI applicationBase) {
		checkNotNull(applicationBase,"Application base URI cannot be null");
		checkArgument(applicationBase.isAbsolute() && !applicationBase.isOpaque(),"Application base URI must be absolute and hierarchical");
		MediaType mediaType=Iterables.getFirst(supportedMediaTypes(),null);
		checkState(mediaType!=null,"No media type providers are available");
		DataTransformator dataTransformation=new DataTransformator();
		dataTransformation.setApplicationBase(applicationBase);
		dataTransformation.setMediaType(mediaType);
		return dataTransformation;
	}

}


/**
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   This file is part of the LDP4j Project:
 *     http://www.ldp4j.org/
 *
 *   Center for Open Middleware
 *     http://www.centeropenmiddleware.com/
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   Copyright (C) 2014-2016 Center for Open Middleware.
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   Licensed 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.
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   Artifact    : org.ldp4j.commons.rmf:rmf-api:0.2.2
 *   Bundle      : rmf-api-0.2.2.jar
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 */
package org.ldp4j.rdf.spi;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.ReflectPermission;
import java.util.Properties;
import java.util.ServiceLoader;
import java.util.concurrent.atomic.AtomicReference;

import org.ldp4j.rdf.Format;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * RuntimeInstance is an abstract factory class that provides various methods for
 * the creation of objects that implement the ALM iStack LDP Kernel SPI (LKS for short).
 * These methods are designed for use by other LKA classes and are not intended
 * to be called directly by applications.
 * RuntimeInstance allows the standard LKA classes to use different LKS
 * implementations without any code changes.

 *

 *
 * An implementation of LKS MUST provide a concrete subclass of RuntimeInstance.
 * Using the supplied RuntimeInstance this can be provided to LKS in one of two
 * ways:

 *

 *
An instance of RuntimeInstance can be instantiated and injected using its
 * static method setInstance. In this case the implementation is responsible
 * for creating the instance; this option is intended for use with
 * implementations based on IoC frameworks.

 *
The class to be used can be configured, (see below). In this case CBLS is
 * responsible for instantiating an instance of the class and the configured
 * class MUST have a public constructor which takes no arguments.

 *

 *
 * Note that an implementation MAY supply an alternate implementation of the
 * RuntimeInstance API class (provided it passes the TCK signature test and
 * behaves according to the specification) that supports alternate means of
 * locating a concrete subclass.

 *

 *
 * A CBLS implementation may rely on a particular implementation of
 * RuntimeInstance being used – applications SHOULD NOT override the supplied
 * RuntimeInstance instance with an application-supplied alternative and doing so
 * may cause unexpected problems.
 *

 */
public abstract class RuntimeInstance {

	private static final String INSTANTIATE_ACTION = "instantiate";

	private static final Logger LOGGER=LoggerFactory.getLogger(RuntimeInstance.class);

	public static final String RDF_API_SPI_RUNTIMEINSTANCE_FINDER = "org.ldp4j.rdf.spi.runtimeinstance.finder";

	/**
	 * Name of the configuration file where the
	 * {@link RuntimeInstance#RDF_API_SPI_PROPERTY} property that
	 * identifies the {@link RuntimeInstance} implementation to be returned from
	 * {@link RuntimeInstance#getInstance()} can be defined.
	 */
	public static final String RDF_API_CFG = "rdf.properties";

	/**
	 * Name of the property identifying the {@link RuntimeInstance} implementation
	 * to be returned from {@link RuntimeInstance#getInstance()}.
	 */
	public static final String RDF_API_SPI_PROPERTY = "org.ldp4j.rdf.spi.RuntimeInstance";

	private static final AtomicReference<RuntimeInstance> CACHED_DELEGATE=new AtomicReference<RuntimeInstance>();

	private static ReflectPermission suppressAccessChecksPermission = new ReflectPermission("suppressAccessChecks");

	/**
	 * Allows custom implementations to extend the {@code RuntimeInstance} class.
	 */
	protected RuntimeInstance() {
	}

	/**
	 * Obtain a {@code RuntimeInstance} instance using the method described in
	 * {@link #getInstance}.
	 *
	 * @return an instance of {@code RuntimeInstance}.
	 */
	private static RuntimeInstance findDelegate() {
		try {
			RuntimeInstance result=createRuntimeInstanceFromSPI();
			if(result==null) {
				result=createRuntimeInstanceFromConfigurationFile();
			}

			if(result==null) {
				final String delegateClassName = System.getProperty(RDF_API_SPI_PROPERTY);
				if(delegateClassName!=null) {
					result=createRuntimeInstanceForClassName(delegateClassName);
				}
			}

			if(result==null) {
				result=new DefaultRuntimeInstance();
			}

			return result;
		} catch (final Exception ex) {
			throw new IllegalStateException("Could not find runtime delegate",ex);
		}
	}

	private static RuntimeInstance createRuntimeInstanceFromConfigurationFile() {
		RuntimeInstance result=null;
		final File configFile = getConfigurationFile();
		if(configFile.canRead()) {
			InputStream is=null;
			try {
				is=new FileInputStream(configFile);
				final Properties configProperties=new Properties();
				configProperties.load(is);
				final String delegateClassName=configProperties.getProperty(RDF_API_SPI_PROPERTY);
				if(delegateClassName!=null) {
					result=createRuntimeInstanceForClassName(delegateClassName);
				}
				if(delegateClassName==null && LOGGER.isWarnEnabled()) {
					LOGGER.warn("Configuration file '"+configFile.getAbsolutePath()+"' does not define a delegate class name");
				}
			} catch(final FileNotFoundException e) {
				if(LOGGER.isDebugEnabled()) {
					LOGGER.debug("Could not find runtime instance configuration file '"+configFile.getAbsolutePath()+"'",e);
				}
			} catch(final IOException e) {
				if(LOGGER.isWarnEnabled()) {
					LOGGER.warn("Could not load runtime instance configuration file '"+configFile.getAbsolutePath()+"'",e);
				}
			} finally {
				closeQuietly(is, "Could find close properties");
			}
		}
		return result;
	}

	/**
	 * Get the configuration file for the Runtime Instance: a file named
	 * {@link RuntimeInstance#RDF_API_CFG} in the lib directory of
	 * current JAVA_HOME.
	 *
	 * @return The configuration file for the runtime instance.
	 */
	private static File getConfigurationFile() {
		return new File(new File(System.getProperty("java.home")),"lib"+File.separator+RDF_API_CFG);
	}

	/**
	 * Close an input stream logging possible failures.
	 * @param is The input stream that is to be closed.
	 * @param message The message to log in case of failure.
	 */
	private static void closeQuietly(final InputStream is, final String message) {
		if(is!=null) {
		try {
			is.close();
		} catch (final Exception e) {
			if(LOGGER.isWarnEnabled()) {
				LOGGER.warn(message,e);
			}
		}
		}
	}

	private static RuntimeInstance createRuntimeInstanceFromSPI() {
		if(!"disable".equalsIgnoreCase(System.getProperty(RDF_API_SPI_RUNTIMEINSTANCE_FINDER))) {
			for (final RuntimeInstance delegate : ServiceLoader.load(RuntimeInstance.class)) {
				return delegate;
			}
		}
		return null;
	}

	private static RuntimeInstance createRuntimeInstanceForClassName(final String delegateClassName) {
		RuntimeInstance result = null;
		try {
			final Class<?> delegateClass = Class.forName(delegateClassName);
			if(RuntimeInstance.class.isAssignableFrom(delegateClass)) {
				final Object impl = delegateClass.newInstance();
				result = RuntimeInstance.class.cast(impl);
			}
		} catch (final ClassNotFoundException e) {
			handleFailure(delegateClassName, "find", e);
		} catch (final InstantiationException e) {
			handleFailure(delegateClassName, INSTANTIATE_ACTION, e);
		} catch (final IllegalAccessException e) {
			handleFailure(delegateClassName, INSTANTIATE_ACTION, e);
		}
		return result;
	}

	/**
	 * @param delegateClassName
	 * @param action
	 * @param failure
	 */
	private static void handleFailure(final String delegateClassName, final String action,
			final Exception failure) {
		if(LOGGER.isWarnEnabled()) {
			LOGGER.warn("Could not "+action+" delegate class "+delegateClassName,failure);
		}
	}

	/**
	 * Obtain a {@code RuntimeInstance} instance. If an instance had not already
	 * been created and set via {@link #setInstance(RuntimeInstance)}, the first
	 * invocation will create an instance which will then be cached for future
	 * use.
	 *
	 *

	 * The algorithm used to locate the RuntimeInstance subclass to use consists
	 * of the following steps:
	 *

	 *

	 *

	 * If a resource with the name of
	 * {@code META-INF/services/org.centeropenmiddleware.almistack.poc.clients.spi.RuntimeInstance} exists, then
	 * its first line, if present, is used as the UTF-8 encoded name of the
	 * implementation class.

	 *

	 * If the $java.home/lib/poc-business-logic.properties file exists and it is readable by
	 * the {@code java.util.Properties.load(InputStream)} method and it contains
	 * an entry whose key is {@code org.centeropenmiddleware.almistack.poc.clients.spi.RuntimeInstance}, then the
	 * value of that entry is used as the name of the implementation class.

	 *

	 * If a system property with the name
	 * {@code org.centeropenmiddleware.almistack.poc.clients.spi.RuntimeInstance} is defined, then its value is
	 * used as the name of the implementation class.

	 *

	 * Finally, a default implementation class name is used.

	 *

	 *
	 * @return an instance of {@code RuntimeInstance}.
	 */
	public static RuntimeInstance getInstance() {
		RuntimeInstance result = RuntimeInstance.CACHED_DELEGATE.get();
		if (result != null) {
			return result;
		}
		synchronized(RuntimeInstance.CACHED_DELEGATE) {
			result=RuntimeInstance.CACHED_DELEGATE.get();
			if(result==null) {
				RuntimeInstance.CACHED_DELEGATE.set(findDelegate());
				result=RuntimeInstance.CACHED_DELEGATE.get();
			}
			return result;
		}
	}

	/**
	 * Set the runtime delegate that will be used by Client Business Logic API
	 * classes. If this method is not called prior to {@link #getInstance} then
	 * an implementation will be sought as described in {@link #getInstance}.
	 *
	 * @param delegate
	 *            the {@code RuntimeInstance} runtime delegate instance.
	 * @throws SecurityException
	 *             if there is a security manager and the permission
	 *             ReflectPermission("suppressAccessChecks") has not been
	 *             granted.
	 */
	public static void setInstance(final RuntimeInstance delegate) {
		final SecurityManager security = System.getSecurityManager();
		if (security != null) {
			security.checkPermission(suppressAccessChecksPermission);
		}
		RuntimeInstance.CACHED_DELEGATE.set(delegate);
	}

	public abstract <T> void registerTransformer(Class<? extends T> type, Transformer<T> transformer);

	public abstract <T> Transformer<T> findTransformer(Class<? extends T> type);

	public abstract <T> Unmarshaller<T> newUnmarshaller(Format format, T output);

	public abstract <T> Marshaller<T> newMarshaller(Format format, T output);

	static class DefaultRuntimeInstance extends RuntimeInstance {

		private static final String ERROR_MESSAGE = String.format("No %s runtime instance found",RuntimeInstance.class);

		@Override
		public <T> void registerTransformer(final Class<? extends T> type, final Transformer<T> transformer) {
			throw new UnsupportedOperationException(ERROR_MESSAGE);
		}

		@Override
		public <T> Transformer<T> findTransformer(final Class<? extends T> type) {
			throw new UnsupportedOperationException(ERROR_MESSAGE);
		}

		@Override
		public <T> Marshaller<T> newMarshaller(final Format format, final T output) {
			throw new UnsupportedOperationException(ERROR_MESSAGE);
		}

		@Override
		public <T> Unmarshaller<T> newUnmarshaller(final Format format, final T output) {
			throw new UnsupportedOperationException(ERROR_MESSAGE);
		}

	}

}