/**
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   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.utils;

import java.math.RoundingMode;
import java.nio.charset.Charset;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.util.Locale;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.google.common.base.CharMatcher;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import com.google.common.math.DoubleMath;

public final class CharsetPreference implements Comparable<CharsetPreference> {

	private static final int    MAX_WEIGHT        = 1000;
	private static final double MAX_WEIGHT_DOUBLE = 1000D;

	private static final char[] CTRL = {
		 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
		10,11,12,13,14,15,16,17,18,19,
		20,21,22,23,24,25,26,27,28,29,
		30,31,
		127
	};

	private static final char[] SEPARATORS = {
		'(' ,  ')' ,  '<' , '>' , '@' , ',' , ';' ,
		':' , '\\' , '\"' , '/' , '[' , ']' , '?' ,
		'=' , '{'  , '}'  , ' ' , '\t'
	};

	private static final CharMatcher TOKEN_MATCHER=
		CharMatcher.
			ASCII.
				and(CharMatcher.noneOf(new String(CTRL))).
				and(CharMatcher.noneOf(new String(SEPARATORS)));

	private static final Pattern QUALITY_PATTERN = Pattern.compile("(q|Q)=((1\\.0{0,3})|(0\\.\\d{0,3}))");
	private static final DecimalFormat FORMAT;

	static {
		final DecimalFormat format = new DecimalFormat("0",new DecimalFormatSymbols(Locale.ENGLISH));
		format.setMaximumFractionDigits(3);
		FORMAT=format;
	}

	private final String charset;
	private final int weight;

	public CharsetPreference(final String charset, final int weight) {
		this.charset = charset;
		this.weight = weight;
	}

	public String charset() {
		return this.charset;
	}

	public double weight() {
		return this.weight/MAX_WEIGHT_DOUBLE;
	}

	public boolean isWildcard() {
		return "*".equals(this.charset);
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public int hashCode() {
		return Objects.hash(this.charset,this.weight);
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public boolean equals(final Object obj) {
		boolean result=false;
		if(obj instanceof CharsetPreference) {
			final CharsetPreference that=(CharsetPreference)obj;
			result=
				Objects.equals(this.charset, that.charset) &&
				Objects.equals(this.weight, that.weight);
		}
		return result;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public String toString() {
		return
			this.charset+
			(this.weight==MAX_WEIGHT?
				"":
				" ; q="+FORMAT.format((float)this.weight/(float)MAX_WEIGHT));
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public int compareTo(final CharsetPreference o) {
		return this.weight-o.weight;
	}

	/**
	 * Create a charset preference that matches the following grammar:
	 *
	 * <pre>
	 *        {@code
	   CHAR           = <any US-ASCII character (octets 0 - 127)>
	   DIGIT          = <any US-ASCII digit "0".."9">
	   CTL            = <any US-ASCII control character
	                    (octets 0 - 31) and DEL (127)>
	   SP             = <US-ASCII SP, space (32)>
	   HT             = <US-ASCII HT, horizontal-tab (9)>
	   <">            = <US-ASCII double-quote mark (34)>
	   token          = 1*<any CHAR except CTLs or separators>
	   separators     = "(" | ")" | "<" | ">" | "@"
	                  | "," | ";" | ":" | "\" | <">
	                  | "/" | "[" | "]" | "?" | "="
	                  | "{" | "}" | SP | HT
	   charset        = token
	   qvalue         = ( "0" [ "." 0*3DIGIT ] )
	                  | ( "1" [ "." 0*3("0") ] )
	   preference     = ( charset | "*" )[ ";" "q" "=" qvalue ]}
	 * </pre>
	 *
	 * @param str
	 *            a charset specifier
	 * @return the matching chartset preference, or {@code null} if no one could
	 *         be found
	 */
	public static CharsetPreference valueOf(final String str) {
		final String[] parts = str.split(";");
		if(parts.length<=2) {
			final String charsetName =parts[0].trim();
			if("*".equals(charsetName) || TOKEN_MATCHER.matchesAllOf(charsetName)) {
				int weight=MAX_WEIGHT;
				if(parts.length==2) {
					final String weightValue=parts[1].trim();
					final Matcher matcher = QUALITY_PATTERN.matcher(weightValue);
					if(!matcher.matches()) {
						return null;
					}
					if(weightValue.charAt(2)=='0') {
						weight=Integer.parseInt(Strings.padEnd(weightValue.substring(4),3,'0'));
					}
				}
				return new CharsetPreference(charsetName,weight);
			}
		}
		return null;
	}

	public static CharsetPreference wildcard() {
		return new CharsetPreference("*",MAX_WEIGHT);
	}

	public static CharsetPreference wildcard(final double weight) {
		return new CharsetPreference("*",round(weight));
	}

	public static CharsetPreference create(final Charset charset) {
		Objects.requireNonNull(charset,"Charset cannot be null");
		return new CharsetPreference(charset.name(),MAX_WEIGHT);
	}

	public static CharsetPreference create(final Charset charset, final double weight) {
		Objects.requireNonNull(charset,"Charset cannot be null");
		return new CharsetPreference(charset.name(),round(weight));
	}

	static int round(final double weight) {
		Preconditions.checkArgument(weight>=0D,"Weight must be greater than or equal to 0 (%s)",weight);
		Preconditions.checkArgument(weight<=1D,"Weight must be lower than or equal to 1 (%s)",weight);
		return DoubleMath.roundToInt(weight*MAX_WEIGHT_DOUBLE,RoundingMode.DOWN);
	}

}


/**
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   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.net.URI;
import java.util.Collections;
import java.util.Set;

import org.ldp4j.application.data.Individual;
import org.ldp4j.application.vocabulary.RDF;
import org.ldp4j.application.vocabulary.Term;

import com.google.common.collect.Sets;

class IndividualHelperImpl implements IndividualHelper {

	private static final class TypeCollector implements ValueVisitor {

		private final class CollectorWorker implements IndividualVisitor {

			/**
			 * {@inheritDoc}
			 */
			@Override
			public void visitManagedIndividual(ManagedIndividual individual) {
				// Discard invalid types
			}

			/**
			 * {@inheritDoc}
			 */
			@Override
			public void visitRelativeIndividual(RelativeIndividual individual) {
				// Discard invalid types
			}

			/**
			 * {@inheritDoc}
			 */
			@Override
			public void visitLocalIndividual(LocalIndividual individual) {
				// Discard invalid types
			}

			/**
			 * {@inheritDoc}
			 */
			@Override
			public void visitExternalIndividual(ExternalIndividual individual) {
				types.add(individual.id());
			}

			/**
			 * {@inheritDoc}
			 */
			@Override
			public void visitNewIndividual(NewIndividual individual) {
				// Discard invalid types
			}

		}

		private final Set<URI> types;
		private final CollectorWorker worker;

		private TypeCollector() {
			this.types=Sets.newLinkedHashSet();
			this.worker = new CollectorWorker();
		}

		private Set<URI> collectedTypes() {
			return this.types;
		}

		/**
		 * {@inheritDoc}
		 */
		@Override
		public void visitLiteral(Literal<?> value) {
			// Discard invalid types
		}

		/**
		 * {@inheritDoc}
		 */
		@Override
		public void visitIndividual(Individual<?, ?> value) {
			value.accept(this.worker);
		}

	}

	private final Individual<?, ?> individual;

	IndividualHelperImpl(Individual<?,?> individual) {
		this.individual = individual;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public Set<URI> types() {
		Property property = this.individual.property(RDF.TYPE.as(URI.class));
		if(property==null) {
			return Collections.emptySet();
		}
		TypeCollector collector = new TypeCollector();
		for(Value value:property) {
			value.accept(collector);
		}
		return collector.collectedTypes();
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public PropertyHelper property(URI propertyId) {
		return new PropertyHelperImpl(propertyId,this.individual);
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public PropertyHelper property(String propertyId) {
		return property(URI.create(propertyId));
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public PropertyHelper property(Term property) {
		return property(property.as(URI.class));
	}

}


/**
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   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-examples:0.2.2
 *   Bundle      : ldp4j-application-examples-0.2.2.jar
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 */
package org.ldp4j.example;

import java.net.URI;
import java.util.Date;
import java.util.concurrent.atomic.AtomicInteger;

import org.ldp4j.application.data.DataSet;
import org.ldp4j.application.data.DataSetHelper;
import org.ldp4j.application.data.DataSetUtils;
import org.ldp4j.application.data.Literals;
import org.ldp4j.application.data.ManagedIndividual;
import org.ldp4j.application.data.ManagedIndividualId;
import org.ldp4j.application.data.Name;
import org.ldp4j.application.data.NamingScheme;
import org.ldp4j.application.ext.ApplicationRuntimeException;
import org.ldp4j.application.session.ContainerSnapshot;
import org.ldp4j.application.session.ResourceSnapshot;
import org.ldp4j.application.session.WriteSession;

/**
 * Base container for managing collections of persons.
 */
public class AbstractPersonContainerHandler extends InMemoryContainerHandler {

	private PersonHandler handler;

	private AtomicInteger id;

	/**
	 * Create a new instance with a given name
	 */
	protected AbstractPersonContainerHandler(String name) {
		super(name);
		this.id=new AtomicInteger();
	}

	/**
	 * Set the person handler associated to this handler.
	 *
	 * @param handler
	 *            the person handler.
	 */
	public final void setHandler(PersonHandler handler) {
		this.handler = handler;
	}

	/**
	 * {@inheritDoc}<br>
	 *
	 * Create a new person resource.
	 */
	@Override
	public final ResourceSnapshot create(ContainerSnapshot container, DataSet representation, WriteSession session) {
		Name<?> name=
			NamingScheme.
				getDefault().
					name(id.incrementAndGet());

		DataSetHelper helper=
					DataSetUtils.newHelper(representation);

		ManagedIndividual individual =
			helper.
				replace(
					DataSetHelper.SELF,
					ManagedIndividualId.createId(name,PersonHandler.ID),
					ManagedIndividual.class);

		individual.
			addValue(
				URI.create("http://www.example.org/vocab#creationDate"),
				Literals.of(new Date()).dateTime());
		try {
			this.handler.add(name, representation);
			ResourceSnapshot member = container.addMember(name);
			session.saveChanges();
			return member;
		} catch (Exception e) {
			this.handler.remove(name);
			throw new ApplicationRuntimeException("Could not create member",e);
		}
	}

}


/**
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   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 java.util.Objects.requireNonNull;

import java.nio.charset.Charset;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.atomic.AtomicReference;

import com.google.common.collect.Maps;

public final class MediaTypes {

	public static final class MediaTypeBuilder {

		private final MediaType mediaType;
		private final Map<String, String> parameters;
		private MediaRangeSyntax syntax;

		public MediaTypeBuilder(MediaType mediaType) {
			this.mediaType = mediaType;
			this.parameters=Maps.newLinkedHashMap(mediaType.parameters());
			this.syntax=preferredSyntax();
		}

		public MediaTypeBuilder withSyntax(MediaRangeSyntax syntax) {
			if(syntax==null) {
				this.syntax=preferredSyntax();
			} else {
				this.syntax=syntax;
			}
			return this;
		}

		public MediaTypeBuilder withCharset(Charset charset) {
			if(charset==null) {
				this.parameters.remove(MediaTypes.PARAM_CHARSET);
			} else {
				this.parameters.put(MediaTypes.PARAM_CHARSET,charset.name());
			}
			return this;
		}

		public MediaTypeBuilder withParam(String name, String value) {
			requireNonNull(name,"Parameter name cannot be null");
			requireNonNull(value,"Parameter value cannot be null");
			this.parameters.put(name, value);
			return this;
		}

		public MediaType build() {
			return
				new ImmutableMediaType(
					this.syntax,
					this.mediaType.type(),
					this.mediaType.subType(),
					this.mediaType.suffix(),
					this.parameters);
		}

	}

	/**
	 * The wildcard type/subtype
	 */
	public static final String WILDCARD_TYPE   = "*";

	private static final String MEDIA_TYPE_CANNOT_BE_NULL           = "Media type cannot be null";
	private static final String TYPE_CANNOT_BE_NULL                 = "Type cannot be null";
	private static final String REFERENCE_MEDIA_TYPE_CANNOT_BE_NULL = "Reference media type cannot be null";
	/**
	 * The key for the standard 'charset' media type parameter
	 */
	public static final String PARAM_CHARSET   = "charset";

	private static final AtomicReference<MediaRangeSyntax> SYNTAX=new AtomicReference<>(MediaRangeSyntax.RFC7230);

	private MediaTypes() {
	}

	/**
	 * Get the preferred syntax used for parsing media types
	 *
	 * @return the syntax
	 */
	public static MediaRangeSyntax preferredSyntax() {
		return SYNTAX.get();
	}

	/**
	 * Set the preferred syntax to use for parsing media types. If the specified
	 * syntax is {@code null}, RFC7230 syntax will be used as default.
	 *
	 * @param syntax
	 *            the syntax
	 */
	public static void preferredSyntax(MediaRangeSyntax syntax) {
		if(syntax==null) {
			SYNTAX.set(MediaRangeSyntax.RFC7230);
		} else {
			SYNTAX.set(syntax);
		}
	}

	/**
	 * Create a wildcard media type
	 *
	 * @return a wildcard media type
	 */
	public static MediaType wildcard() {
		return new ImmutableMediaType(MediaTypes.preferredSyntax(),WILDCARD_TYPE,WILDCARD_TYPE,null,null);
	}

	/**
	 * Create a wildcard media type for a given primary type
	 *
	 * @param type
	 *            The media type primary type
	 * @return a wildcard media type for the given primary type
	 * @throws NullPointerException
	 *             if the type is null
	 * @throws IllegalArgumentException
	 *             if the specified type is not valid
	 */
	public static MediaType wildcard(String type) {
		requireNonNull(type,TYPE_CANNOT_BE_NULL);
		return new ImmutableMediaType(MediaTypes.preferredSyntax(),type,WILDCARD_TYPE,null,null);
	}

	/**
	 * Create a wildcard structured-syntax media type for a given primary type
	 *
	 * @param type
	 *            The media type primary type
	 * @param suffix
	 *            The suffix associated to the structured-syntax
	 * @return a wildcard media type for the given primary type and suffix
	 * @throws NullPointerException
	 *             if the type or suffix is null
	 * @throws IllegalArgumentException
	 *             if the specified type or suffix is not valid
	 */
	public static MediaType wildcard(String type, String suffix) {
		requireNonNull(type,TYPE_CANNOT_BE_NULL);
		requireNonNull(suffix,"Suffix cannot be null");
		return new ImmutableMediaType(MediaTypes.preferredSyntax(),type,WILDCARD_TYPE,suffix,null);
	}

	/**
	 * Parse the given String into a single {@code MediaType}.
	 *
	 * @param mediaType
	 *            the string to parse
	 * @return the mime type
	 * @throws InvalidMediaTypeException
	 *             if the string cannot be parsed
	 */
	public static MediaType fromString(final String mediaType) {
		return ImmutableMediaType.fromString(mediaType, MediaTypes.preferredSyntax());
	}

	/**
	 * Create a media type
	 *
	 * @param type
	 *            The media type primary type
	 * @param subtype
	 *            The media type subtype
	 * @return A media type for the specified media range
	 * @throws NullPointerException
	 *             if any of the specified media range elements is null
	 * @throws InvalidMediaTypeException
	 *             if any of the specified media range elements is not valid
	 */
	public static MediaType of(String type, String subtype) {
		requireNonNull(type,TYPE_CANNOT_BE_NULL);
		requireNonNull(subtype,"Subtype cannot be null");
		return fromString(type+"/"+subtype);
	}

	/**
	 * Create a structured-syntax media type
	 *
	 * @param type
	 *            The media type primary type
	 * @param subtype
	 *            The media type subtype
	 * @param suffix
	 *            The suffix associated to the structured-syntax
	 * @return A media type for the specified media range
	 * @throws NullPointerException
	 *             if any of the specified media range elements is null
	 * @throws InvalidMediaTypeException
	 *             if any of the specified media range elements is not valid
	 */
	public static MediaType of(String type, String subtype, String suffix) {
		requireNonNull(type,TYPE_CANNOT_BE_NULL);
		requireNonNull(subtype,"Subtype cannot be null");
		requireNonNull(suffix,"Suffix cannot be null");
		return fromString(type+"/"+subtype+"+"+suffix);
	}

	/**
	 * Create a {@code MediaTypeBuilder} from a given media type.
	 *
	 * @param mediaType
	 *            The media type to use as builder configuration
	 * @return a builder instance prepopulated with the specified media type.
	 * @throws NullPointerException
	 *             if the media type is null
	 */
	public static MediaTypeBuilder from(MediaType mediaType) {
		requireNonNull(mediaType,"Media type cannot be null");
		return new MediaTypeBuilder(mediaType);
	}

	/**
	 * Indicates whether the {@linkplain MediaType#type() type} is the
	 * wildcard character *.
	 *
	 * @param mediaType
	 *            the instance to verify
	 * @return whether the type of the specified instance is a wildcard
	 * @throws NullPointerException
	 *             if the specified instance is null
	 */
	public static boolean isWildcardType(final MediaType mediaType) {
		requireNonNull(mediaType,MEDIA_TYPE_CANNOT_BE_NULL);
		return WILDCARD_TYPE.equals(mediaType.type());
	}

	/**
	 * Indicates whether the {@linkplain MediaType#subType() subtype} is the
	 * wildcard character *.
	 *
	 * @param mediaType
	 *            the instance to verify
	 * @return whether the subtype of the specified instance is a wildcard
	 * @throws NullPointerException
	 *             if the specified instance is null
	 */
	public static boolean isWildcardSubType(final MediaType mediaType) {
		requireNonNull(mediaType,MEDIA_TYPE_CANNOT_BE_NULL);
		return WILDCARD_TYPE.equals(mediaType.subType());
	}

	/**
	 * Indicates whether the {@linkplain MediaType#suffix() suffix} is
	 * {@code null}.
	 *
	 * @param mediaType
	 *            the instance to verify
	 * @return {@code true} if the suffix of the specified instance is not
	 *         {@code null}; {@code false} otherwise.
	 * @throws NullPointerException
	 *             if the specified instance is null
	 */
	public static boolean isStructured(final MediaType mediaType) {
		requireNonNull(mediaType,MEDIA_TYPE_CANNOT_BE_NULL);
		return mediaType.suffix()!=null;
	}

	/**
	 * Indicate whether the former {@code MediaType} includes the latter.
	 *

	 * For instance, {@code text/*} includes {@code text/plain} and
	 * {@code text/html}, and {@code application/*+xml} includes
	 * {@code application/soap+xml}, etc. This method is not symmetric.
	 *
	 * @param one
	 *            the reference media type with which to compare
	 * @param other
	 *            the media type to compare
	 * @return {@code true} if the reference media type includes the compared
	 *         media type; {@code false} otherwise
	 */
	public static boolean includes(final MediaType one, final MediaType other) {
		return areCompatibleMediaTypes(one,other,false);
	}

	/**
	 * Indicate whether the former {@code MediaType} is compatible with the
	 * latter.
	 *

	 * For instance, {@code text/*} is compatible with {@code text/plain},
	 * {@code text/html}, and vice versa. In effect, this method is similar to
	 * {@link #includes}, except that it is symmetric.
	 *
	 * @param one
	 *            the reference media type with which to compare
	 * @param other
	 *            the media type to compare
	 * @return {@code true} if both media types are compatible; {@code false}
	 *         otherwise
	 */
	public static boolean areCompatible(final MediaType one, final MediaType other) {
		return areCompatibleMediaTypes(one,other,true);
	}

	/**
	 * Format the media type in a HTTP-header compliant manner using preferred
	 * format.
	 *
	 * @param mediaType
	 *            the media type to format
	 * @return the compliant representation of the media type
	 * @see [RFC
	 *      7231] Hypertext Transfer Protocol (HTTP/1.1): Semantics and Content,
	 *      section 3.1.1.1
	 */
	public static String toHeader(final MediaType mediaType) {
		requireNonNull(mediaType,REFERENCE_MEDIA_TYPE_CANNOT_BE_NULL);
		final StringBuilder builder=
			new StringBuilder().
				append(mediaType.type().toLowerCase(Locale.ENGLISH)).
				append('/').
				append(mediaType.subType().toLowerCase(Locale.ENGLISH));
		final String suffix=mediaType.suffix();
		if(suffix!=null) {
			builder.append('+').append(suffix.toLowerCase(Locale.ENGLISH));
		}
		final Charset charset=mediaType.charset();
		if(charset!=null) {
			builder.append(";charset=").append(charset.name().toLowerCase(Locale.ENGLISH));
		}
		for(Entry<String,String> entry:mediaType.parameters().entrySet()) {
			final String key=entry.getKey();
			if(isStandardParameter(key)) {
				continue;
			}
			builder.append(';').append(key.toLowerCase(Locale.ENGLISH)).append('=').append(entry.getValue());
		}
		return builder.toString();
	}

	/**
	 * Check whether or not the specified parameter is a standard Media Type
	 * parameter
	 *
	 * @param parameter
	 *            the parameter to check
	 * @return {@code true} if the parameter is standard, or {@code false}
	 *         otherwise.
	 */
	public static boolean isStandardParameter(final String parameter) {
		return PARAM_CHARSET.equalsIgnoreCase(parameter);
	}

	private static boolean areCompatibleMediaTypes(final MediaType one, final MediaType other, boolean symmetric) {
		requireNonNull(one,REFERENCE_MEDIA_TYPE_CANNOT_BE_NULL);
		if(other==null) {
			return false;
		}
		return haveCompatibleMediaRange(one, other, symmetric);
	}

	private static boolean haveCompatibleMediaRange(final MediaType one, final MediaType other, boolean symmetric) {
		if(isWildcardType(one)) {
			return true;
		}
		if(symmetric && isWildcardType(other)) {
			return true;
		}
		if(!equalsIgnoreCase(one.suffix(), other.suffix())) {
			return false;
		}
		return
			one.type().equalsIgnoreCase(other.type()) &&
			haveCompatibleSubtype(one, other, symmetric);
	}

	private static boolean haveCompatibleSubtype(final MediaType one, final MediaType other, boolean symmetric) {
		if(isWildcardSubType(one)) {
			return true;
		}
		if(symmetric && isWildcardSubType(other)) {
			return true;
		}
		return one.subType().equalsIgnoreCase(other.subType());
	}

	private static boolean equalsIgnoreCase(String str, String anotherStr) {
		return str==null ? anotherStr==null : str.equalsIgnoreCase(anotherStr);
	}

}


/**
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   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-mem:0.2.2
 *   Bundle      : ldp4j-application-kernel-mem-0.2.2.jar
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 */
package org.ldp4j.application.kernel.impl;

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

import java.util.Date;

import org.ldp4j.application.data.Name;
import org.ldp4j.application.engine.context.EntityTag;
import org.ldp4j.application.kernel.endpoint.Endpoint;
import org.ldp4j.application.kernel.resource.Resource;
import org.ldp4j.application.kernel.resource.ResourceId;
import org.ldp4j.application.kernel.spi.ModelFactory;
import org.ldp4j.application.kernel.template.ContainerTemplate;
import org.ldp4j.application.kernel.template.ResourceTemplate;
import org.ldp4j.application.kernel.template.SimpleTemplateVisitor;
import org.ldp4j.application.kernel.template.TemplateLibrary;

final class InMemoryModelFactory implements ModelFactory {

	private final class RootResourceCreator extends SimpleTemplateVisitor {

		private final ResourceId id;
		private InMemoryResource resource;

		private RootResourceCreator(ResourceId id) {
			this.id = id;
		}

		private InMemoryResource createdResource() {
			return this.resource;
		}

		private void createResource(InMemoryResource resource) {
			this.resource=resource;
			this.resource.setTemplateLibrary(InMemoryModelFactory.this.templateLibrary);
		}

		/**
		 * {@inheritDoc}
		 */
		@Override
		public void visitResourceTemplate(ResourceTemplate template) {
			createResource(new InMemoryResource(id,null));
		}

		/**
		 * {@inheritDoc}
		 */
		@Override
		public void visitContainerTemplate(ContainerTemplate template) {
			createResource(new InMemoryContainer(id,null));
		}

	}

	private TemplateLibrary templateLibrary;

	InMemoryModelFactory() {
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void useTemplates(TemplateLibrary library) {
		this.templateLibrary=library;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public Resource createResource(ResourceTemplate template, Name<?> name) {
		checkNotNull(name,"Resource name cannot be null");
		checkArgument(this.templateLibrary.contains(template),"Unknown template "+template);
		RootResourceCreator creator=
			new RootResourceCreator(
				ResourceId.createId(name,template));
		template.accept(creator);
		return creator.createdResource();
	}

	@Override
	public Endpoint createEndpoint(String path, Resource resource, Date created, EntityTag entityTag) {
		checkNotNull(resource,"Resource cannot be null");
		return InMemoryEndpoint.create(path, resource.id(), created, entityTag);
	}

}


/**
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   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-examples:0.2.2
 *   Bundle      : ldp4j-application-examples-0.2.2.jar
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 */
package org.ldp4j.example;

import java.net.URI;
import java.util.Date;

import org.ldp4j.application.ApplicationContext;
import org.ldp4j.application.data.DataSet;
import org.ldp4j.application.data.Individual;
import org.ldp4j.application.data.Literals;
import org.ldp4j.application.data.ManagedIndividualId;
import org.ldp4j.application.data.Name;
import org.ldp4j.application.session.ResourceSnapshot;
import org.ldp4j.application.session.SnapshotResolver;
import org.ldp4j.application.session.WriteSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Utility class that demonstrates how to resolve at runtime an snapshot address
 * and viceversa.
 */
public final class DynamicResourceResolver implements Runnable {

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

	/** Predicate used for exposing whether or not the resolution roundtrip works */
	public static final URI SNAPSHOT_RESOLUTION = URI.create("http://www.ldp4j.org/ns#snapshotResolution");

	/** Predicate used for exposing the URI used for publishing the resource */
	public static final URI SNAPSHOT_ENDPOINT = URI.create("http://www.ldp4j.org/ns#snapshotEndpoint");

	/** Canonical base URI used for resolving the resources */
	public static final URI CANONICAL_BASE = URI.create("http://www.ldp4j.org/fixture/ldp4j/api/");

	private DynamicResourceHandler handler;
	private Name<String> name;

	/**
	 * Create a new instance with a handler and a resource name.
	 *
	 * @param handler
	 *            the handler that holds the resource to be resolved.
	 * @param name
	 *            the name of the resource to be resolved.
	 */
	public DynamicResourceResolver(DynamicResourceHandler handler, Name<String> name) {
		this.handler = handler;
		this.name = name;
	}

	/**
	 * Update the resource representation adding the path where the resource is
	 * published and whether or not if given that URI it can be resolved to the
	 * same resource.
	 */
	@Override
	public void run() {
		ApplicationContext ctx = ApplicationContext.getInstance();
		LOGGER.debug("Starting resolution process on {}...",new Date());
		try(WriteSession session=ctx.createSession()) {
			ResourceSnapshot snapshot=
				session.find(
					ResourceSnapshot.class,
					this.name,
					DynamicResourceHandler.class);

			DataSet dataSet = this.handler.get(snapshot);
			Individual<?,?> individual =
				dataSet.
					individualOfId(ManagedIndividualId.createId(snapshot.name(),snapshot.templateId()));

			SnapshotResolver snapshotResolver =
				SnapshotResolver.
					builder().
						withReadSession(session).
						withCanonicalBase(CANONICAL_BASE).
						build();
			URI snapshotEndpoint = snapshotResolver.toURI(snapshot);

			individual.
				addValue(
					SNAPSHOT_ENDPOINT,
					Literals.newLiteral(snapshotEndpoint));
			individual.
				addValue(
					SNAPSHOT_RESOLUTION,
					Literals.newLiteral(roundtrip(snapshotResolver,snapshotEndpoint,snapshot)));

			this.handler.update(this.name, dataSet);

			session.modify(snapshot);
			session.saveChanges();
		} catch (Exception e) {
			LOGGER.error("Could not resolve resource",e);
		} finally {
			LOGGER.debug("Finalized resolution process");
		}
	}

	private String roundtrip(SnapshotResolver snapshotResolver, URI snapshotEndpoint, ResourceSnapshot original) {
		ResourceSnapshot resolved = snapshotResolver.fromURI(snapshotEndpoint);
		return
			resolved!=null &&
			resolved.name().equals(original.name()) &&
			resolved.handlerClass()==original.handlerClass() ?
				"OK" :
				"KO";
	}

}


/**
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   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.vocabulary;

import java.util.Objects;

/**
 * A term implementation that enforces the creation of immutable terms defined
 * for an {@code AbstractImmutableVocabulary}, which behave as {@code Enum} constants.
 *
 * @version 1.0
 * @since 1.0.0
 * @author Miguel Esteban Gutiérrez
 * @see AbstractImmutableVocabulary
 */
public class ImmutableTerm implements Term {

	private static final long serialVersionUID = 6842492155071877731L;

	private static final int HASH_CODE_SALT = 19;
	private final int ordinal;
	private final String name;
	private final AbstractImmutableVocabulary<? extends ImmutableTerm> vocabulary;
	private final String entityName;

	/**
	 * Create a new term for a vocabulary and entity.
	 *
	 * @param vocabulary
	 *            the vocabulary to which the term will belong to.
	 * @param entityName
	 *            the entity name of the term.
	 * @throws NullPointerException
	 *             if any of the vocabulary is {@code null}.
	 * @throws IllegalArgumentException
	 *             if the entity name is not valid.
	 */
	public ImmutableTerm(AbstractImmutableVocabulary<? extends ImmutableTerm> vocabulary, String entityName) {
		this.vocabulary = Objects.requireNonNull(vocabulary,"Vocabulary cannot be null");
		this.entityName = entityName;
		this.name = TermUtils.toTermName(entityName);
		this.ordinal = vocabulary.reserveTermName(this.name);
		vocabulary.registerTerm(this);
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public final int ordinal() {
		return ordinal;
	}

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

	/**
	 * {@inheritDoc}
	 */
	@Override
	public final String entityName() {
		return entityName;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public final String qualifiedEntityName() {
		return vocabulary.getNamespace() + entityName;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public final Vocabulary getDeclaringVocabulary() {
		return vocabulary;
	}

	/**
	 * {@inheritDoc} <br>
	 * This method may be overridden, though it typically isn't necessary or
	 * desirable. An term type should override this method when a more specific
	 * value types are supported.
	 */
	@Override
	public <V> V as(Class<? extends V> valueClass) {
		try {
			return TypeAdapter.adapt(this, valueClass);
		} catch (CannotAdaptClassesException e) {
			throw new UnsupportedOperationException("Class '"+getClass().getCanonicalName()+" cannot be transformed to '"+valueClass.getCanonicalName()+"'",e);
		}
	}

	/**
	 * Returns true if the specified object is equal to this term constant.
	 *
	 * @param other
	 *            the object to be compared for equality with this object.
	 * @return true if the specified object is equal to this term constant.
	 */
	@Override
	public final boolean equals(Object other) {
		return this == other;
	}

	/**
	 * Returns a hash code for this term constant.
	 *
	 * @return a hash code for this term constant.
	 */
	@Override
	public final int hashCode() {
		return super.hashCode()*HASH_CODE_SALT;
	}

	/**
	 * Throws CloneNotSupportedException. This guarantees that terms are never
	 * cloned, which is necessary to preserve their "singleton" status.
	 *
	 * @return (never returns)
	 */
	@Override
	protected final Object clone() throws CloneNotSupportedException { // NOSONAR
		throw new CloneNotSupportedException();
	}

	/**
	 * Returns the qualified entity name of this term constant.
	 * This method may be overridden, though it typically isn't necessary or
	 * desirable. An term type should override this method when a more
	 * "programmer-friendly" string form exists.
	 *
	 * @return the name of this term constant
	 */
	@Override
	public String toString() {
		return qualifiedEntityName();
	}

	/**
	 * Compares this term with the specified object for order. Returns a
	 * negative integer, zero, or a positive integer as this object is less
	 * than, equal to, or greater than the specified object.
	 *
	 * Term constants are only comparable to other term constants of the same
	 * vocabulary. The natural order implemented by this method is the order in
	 * which the constants are declared.
	 */
	@Override
	public int compareTo(Term other) {
		ImmutableTerm self = this;
		if(self.getDeclaringVocabulary() != other.getDeclaringVocabulary()) {
			throw new ClassCastException();
		}
		return self.ordinal - other.ordinal();
	}

}


/**
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   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-examples:0.2.2
 *   Bundle      : ldp4j-application-examples-0.2.2.jar
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 */
package org.ldp4j.example;

import org.ldp4j.application.data.DataSet;
import org.ldp4j.application.data.Name;
import org.ldp4j.application.ext.ApplicationRuntimeException;
import org.ldp4j.application.ext.annotations.IndirectContainer;
import org.ldp4j.application.ext.annotations.MembershipRelation;
import org.ldp4j.application.session.ContainerSnapshot;
import org.ldp4j.application.session.ResourceSnapshot;
import org.ldp4j.application.session.WriteSession;

/**
 * Example indirect container template handler.
 */
@IndirectContainer(
	id = BookContainerHandler.ID,
	memberHandler = BookHandler.class,
	membershipRelation=MembershipRelation.HAS_MEMBER,
	membershipPredicate="http://www.ldp4j.org/vocabularies/example#hasBook",
	insertedContentRelation = BookContainerHandler.INSERTED_CONTENT_RELATION
)
public class BookContainerHandler extends InMemoryContainerHandler {

	/**
	 * The inserted content relation of the template defined by the handler.
	 */
	public static final String INSERTED_CONTENT_RELATION = "http://www.ldp4j.org/vocabularies/example#bookshelf";

	/**
	 * The template identifier of the handler.
	 */
	public static final String ID="bookContainerTemplate";

	private BookHandler handler;

	/**
	 * Create a new instance.
	 */
	public BookContainerHandler() {
		super("BookContainer");
	}

	/**
	 * Set the book handler associated to this handler.
	 *
	 * @param handler
	 *            the book handler.
	 */
	public void setBookHandler(BookHandler handler) {
		this.handler = handler;
	}

	/**
	 * Return the book handler associated to this handler.
	 *
	 * @return the book handler associated to this handler.
	 * @throws IllegalStateException
	 *             if no associated book handler has been defined.
	 */
	public BookHandler bookHandler() {
		if(this.handler==null) {
			throw new IllegalStateException("Handler not initialized yet");
		}
		return this.handler;
	}

	/**
	 * {@inheritDoc}<br>
	 *
	 * Create a new book resource.
	 */
	@Override
	public ResourceSnapshot create(ContainerSnapshot container, DataSet representation, WriteSession session) {
		NameProvider nameProvider = nameProvider(container.name());
		Name<?> nextName = nameProvider.nextMemberName();
		try {
			bookHandler().add(nextName,representation);
			ResourceSnapshot newMember = container.addMember(nextName);
			session.saveChanges();
			return newMember;
		} catch (Exception e) {
			bookHandler().remove(nextName);
			throw new ApplicationRuntimeException("Could not create member",e);
		}
	}

}


/**
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   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-examples:0.2.2
 *   Bundle      : ldp4j-application-examples-0.2.2.jar
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 */
package org.ldp4j.example;

import java.util.LinkedHashMap;
import java.util.Map;

import org.ldp4j.application.data.DataSet;
import org.ldp4j.application.data.Name;
import org.ldp4j.application.ext.ResourceHandler;
import org.ldp4j.application.ext.UnknownResourceException;
import org.ldp4j.application.session.ResourceSnapshot;

/**
 * An example in-memory resource handler.
 */
public abstract class InMemoryResourceHandler implements ResourceHandler {

	private final String handlerName;
	private final Map<Name<?>, DataSet> resources;

	protected InMemoryResourceHandler(String handlerName) {
		this.handlerName=handlerName;
		this.resources=new LinkedHashMap<Name<?>,DataSet>();
	}

	protected final String getHandlerName() {
		return handlerName;
	}

	/**
	 * {@inheritDoc}
	 * @throws UnknownResourceException if the resource is not known.
	 */
	@Override
	public DataSet get(ResourceSnapshot resource) throws UnknownResourceException {
		DataSet dataSet = this.resources.get(resource.name());
		if(dataSet==null) {
			throw new UnknownResourceException("Unknown resource '"+resource.name()+"'");
		}
		return dataSet;
	}

	/**
	 * Create a new resource to the collection of resource managed by the
	 * handler. If the name is already in use, the representation will be
	 * updated.
	 *
	 * @param name
	 *            the name of the new resource.
	 * @param data
	 *            the default representation for the resource.
	 */
	public final void add(Name<?> name, DataSet data) {
		this.resources.put(name,data);
	}

	/**
	 * Update the representation of a resource managed by the handler.
	 *
	 * @param name
	 *            the name of the resource.
	 * @param data
	 *            the new representation.
	 */
	public final void update(Name<?> name, DataSet data) {
		remove(name);
		add(name,data);
	}

	/**
	 * Remove all the resources managed by the handler.
	 */
	public final void clear() {
		this.resources.clear();
	}

	/**
	 * Return the number or resources managed by the handler.
	 *
	 * @return the number of resources managed by the handler.
	 */
	public final int size() {
		return this.resources.size();
	}

	/**
	 * Remove a resource managed by the handler.
	 *
	 * @param name
	 *            the name of the resource to be removed.
	 */
	public final void remove(Name<?> name) {
		this.resources.remove(name);
	}

	/**
	 * Return {@code true} if the handler manages a given resource,
	 * {@code false} otherwise.
	 *
	 * @param resourceName
	 *            the name of the resource
	 * @return whether or not the handler manages the specified resource.
	 */
	public final boolean hasResource(Name<?> resourceName) {
		return this.resources.containsKey(resourceName);
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public String toString() {
		return getHandlerName();
	}

}


/**
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   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-mem:0.2.2
 *   Bundle      : ldp4j-application-kernel-mem-0.2.2.jar
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 */
package org.ldp4j.application.kernel.impl;

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

import java.util.Map;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

import org.ldp4j.application.kernel.constraints.ConstraintReport;
import org.ldp4j.application.kernel.constraints.ConstraintReportId;
import org.ldp4j.application.kernel.constraints.ConstraintReportRepository;
import org.ldp4j.application.kernel.lifecycle.LifecycleException;
import org.ldp4j.application.kernel.lifecycle.Managed;
import org.ldp4j.application.kernel.resource.Resource;
import org.ldp4j.application.kernel.resource.ResourceId;

import com.google.common.collect.LinkedHashMultimap;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;

final class InMemoryConstraintReportRepository implements Managed, ConstraintReportRepository {

	private final ReadWriteLock lock=new ReentrantReadWriteLock();
	private final Multimap<ResourceId, String> failureIds;
	private final Map<ConstraintReportId,ConstraintReport> reports;

	InMemoryConstraintReportRepository() {
		this.reports=Maps.newLinkedHashMap();
		this.failureIds=LinkedHashMultimap.create();
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public ConstraintReport constraintReportOfId(ConstraintReportId id) {
		checkNotNull(id,"Constraint report identifier cannot be null");
		this.lock.readLock().lock();
		try {
			return this.reports.get(id);
		} finally {
			this.lock.readLock().unlock();
		}
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void add(ConstraintReport report) {
		checkNotNull(report,"Constraint report cannot be null");
		this.lock.writeLock().lock();
		try {
			ConstraintReportId id = report.id();
			this.failureIds.put(id.resourceId(),id.failureId());
			this.reports.put(id,report);
		} finally {
			this.lock.writeLock().unlock();
		}
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void remove(ConstraintReport report) {
		checkNotNull(report,"Constraint report cannot be null");
		this.lock.writeLock().lock();
		try {
			ConstraintReportId reportId = report.id();
			this.failureIds.remove(reportId.resourceId(),reportId.failureId());
			this.reports.remove(reportId);
		} finally {
			this.lock.writeLock().unlock();
		}
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void removeByResource(Resource resource) {
		checkNotNull(resource,"Resource cannot be null");
		this.lock.writeLock().lock();
		try {
			ResourceId resourceId = resource.id();
			for(String constraintsId:this.failureIds.get(resourceId)) {
				this.reports.remove(ConstraintReportId.create(resourceId, constraintsId));
			}
			this.failureIds.removeAll(resourceId);
		} finally {
			this.lock.writeLock().unlock();
		}
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void init() throws LifecycleException {
		// Nothing to do
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void shutdown() throws LifecycleException {
		this.lock.writeLock().lock();
		try {
			this.reports.clear();
		} finally {
			this.lock.writeLock().unlock();
		}
	}

}


/**
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   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-core:0.2.2
 *   Bundle      : ldp4j-application-kernel-core-0.2.2.jar
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 */
package org.ldp4j.application.kernel.template;

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

import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

import org.ldp4j.application.ext.ResourceHandler;
import org.ldp4j.application.kernel.template.AttachedTemplate;
import org.ldp4j.application.kernel.template.ResourceTemplate;

import com.google.common.base.MoreObjects;
import com.google.common.base.MoreObjects.ToStringHelper;
import com.google.common.base.Optional;

public abstract class AbstractMutableTemplate<T extends ResourceHandler> implements ResourceTemplate {

	private final String id;
	private final Class<? extends T> handlerClass;
	private final Map<String,AttachedTemplate> attachments=new LinkedHashMap<String,AttachedTemplate>();

	private String name;
	private String description;

	protected AbstractMutableTemplate(String id, Class<? extends T> handlerClass) {
		checkNotNull(id,"Template id cannot be null");
		checkArgument(!id.trim().isEmpty(),"Template id cannot be empty");
		this.id=id.trim();
		this.handlerClass = handlerClass;
	}

	void setName(String name) {
		this.name=name;
	}

	void setDescription(String description) {
		this.description=description;
	}

	MutableAttachedTemplate attachTemplate(String attachmentId, ResourceTemplate template, String path) {
		checkNotNull(attachmentId, "AttachmentSnapshot identifier cannot be null");
		checkNotNull(template, "Attached template cannot be null");
		checkNotNull(path, "Attached template path cannot be null");
		checkArgument(!attachments.containsKey(path),"Another template is attached at path '%s'",path);
		MutableAttachedTemplate newAttachment = new MutableAttachedTemplate(attachmentId,template,path);
		this.attachments.put(attachmentId, newAttachment);
		return newAttachment;
	}

	void detachTemplate(String attachmentId) {
		checkNotNull(attachmentId, "Attached template id cannot be null");
		checkArgument(attachments.containsKey(attachmentId),"No template '%s' is attached template",attachmentId);
		this.attachments.remove(attachmentId);
	}

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

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

	/**
	 * {@inheritDoc}
	 */
	@Override
	public Optional<String> name() {
		return Optional.fromNullable(name);
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public Optional<String> description() {
		return Optional.fromNullable(description);
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public Set<AttachedTemplate> attachedTemplates() {
		return Collections.unmodifiableSet(new LinkedHashSet<AttachedTemplate>(attachments.values()));
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public AttachedTemplate attachedTemplate(String attachmentId) {
		checkNotNull(attachmentId, "AttachmentSnapshot identifier cannot be null");
		return attachments.get(attachmentId);
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public Iterator<AttachedTemplate> iterator() {
		return Collections.unmodifiableCollection(attachments.values()).iterator();
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public Set<String> attachmentIds() {
		return Collections.unmodifiableSet(attachments.keySet());
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public int hashCode() {
		return
			Objects.
				hash(this.id,this.handlerClass);
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public boolean equals(Object obj) {
		boolean result=false;
		if(obj!=null && this.getClass()==obj.getClass()) {
			AbstractMutableTemplate<?> that = (AbstractMutableTemplate<?>) obj;
			result=
				Objects.equals(this.id, that.id) &&
				Objects.equals(this.handlerClass, that.handlerClass);
		}
		return result;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public String toString() {
		return
			stringHelper().
				toString();
	}

	protected ToStringHelper stringHelper() {
		return
			MoreObjects.
				toStringHelper(getClass()).
					omitNullValues().
					add("id",this.id).
					add("handlerClass",this.handlerClass.getCanonicalName()).
					add("name",this.name).
					add("description",this.description).
					add("attachments", this.attachments);
	}

}


/**
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   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 java.util.Set;

import org.ldp4j.application.vocabulary.Term;

final class IndividualPropertyHelperImpl implements IndividualPropertyHelper {

	private final IndividualHelper iHelper;
	private final PropertyHelper pHelper;

	IndividualPropertyHelperImpl(IndividualHelper iHelper, PropertyHelper pHelper) {
		this.iHelper = iHelper;
		this.pHelper = pHelper;
	}

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

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

	/**
	 * {@inheritDoc}
	 */
	@Override
	public IndividualHelper firstIndividual() {
		return this.pHelper.firstIndividual();
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public Set<URI> types() {
		return this.iHelper.types();
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public PropertyHelper property(URI propertyId) {
		return this.iHelper.property(propertyId);
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public PropertyHelper property(String propertyId) {
		return this.iHelper.property(propertyId);
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public PropertyHelper property(Term property) {
		return this.iHelper.property(property);
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public IndividualPropertyHelper withLiteral(Object rawValue) {
		return this.pHelper.withLiteral(rawValue);
	}

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

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

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

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

	/**
	 * {@inheritDoc}
	 */
	@Override
	public IndividualPropertyHelper withIndividual(URI id) {
		return this.pHelper.withIndividual(id);
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public IndividualPropertyHelper withIndividual(String id) {
		return this.pHelper.withIndividual(id);
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public IndividualPropertyHelper withIndividual(Term id) {
		return this.pHelper.withIndividual(id);
	}

}


/**
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   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-examples:0.2.2
 *   Bundle      : ldp4j-application-examples-0.2.2.jar
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 */
package org.ldp4j.example;

import java.util.ArrayList;
import java.util.Deque;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import org.ldp4j.application.data.Name;
import org.ldp4j.application.ext.ApplicationRuntimeException;

/**
 * Utility class for managing the collections of names that can be used for
 * identifying the resources upon creation at runtime.
 */
public final class NameProvider {

	private final class NameSource {

		private final Deque<Name<String>> pendingNames;
		private final String tag;

		private NameSource(String tag) {
			this.tag = tag;
			this.pendingNames=new LinkedList<Name<String>>();
		}

		private Name<String> nextName() {
			if(this.pendingNames.isEmpty()) {
				throw new ApplicationRuntimeException(String.format("No more %s names available for resource '%s'",tag,NameProvider.this.owner));
			}
			return this.pendingNames.pop();
		}

		private void addName(Name<String> name) {
			this.pendingNames.addLast(name);
		}

	}

	private final Name<String> owner;
	private final Map<String,NameSource> attachmentNameSources;
	private final NameSource resourceNamesSource;
	private final NameSource memberNamesSource;

	private NameProvider(Name<String> owner) {
		this.owner = owner;
		this.attachmentNameSources=new LinkedHashMap<String, NameSource>();
		this.resourceNamesSource=new NameSource("resource");
		this.memberNamesSource=new NameSource("member");
	}

	private NameSource nameSource(String attachmentId) {
		NameSource result = this.attachmentNameSources.get(attachmentId);
		if(result==null) {
			result=new NameSource("attachment <<"+attachmentId+">>");
			this.attachmentNameSources.put(attachmentId, result);
		}
		return result;
	}

	/**
	 * Return the name of the owner of the name provider.
	 *
	 * @return the name of the name provider.
	 */
	public Name<String> owner() {
		return this.owner;
	}

	/**
	 * Return the pending names for the specified attachment.
	 *
	 * @param attachmentId
	 *            the name of the attachment.
	 * @return the pending names for the specified attachment.
	 */
	public List<Name<String>> pendingAttachmentNames(String attachmentId) {
		List<Name<String>> result = new ArrayList<Name<String>>();
		NameSource source = this.attachmentNameSources.get(attachmentId);
		if(source!=null) {
			result.addAll(source.pendingNames);
		}
		return result;
	}

	/**
	 * Return the pending resource names.
	 *
	 * @return the pending resource names.
	 */
	public List<Name<String>> pendingResourceNames() {
		return new ArrayList<Name<String>>(this.resourceNamesSource.pendingNames);
	}

	/**
	 * Return the pending member names.
	 *
	 * @return the pending member names.
	 */
	public List<Name<String>> pendingMemberNames() {
		return new ArrayList<Name<String>>(this.memberNamesSource.pendingNames);
	}

	/**
	 * Add a resource name to the list of available resource names.
	 *
	 * @param nextName
	 *            the resource name.
	 */
	public void addResourceName(Name<String> nextName) {
		this.resourceNamesSource.addName(nextName);
	}

	/**
	 * Add a member name to the list of available member names.
	 *
	 * @param nextName
	 *            the member name.
	 */
	public void addMemberName(Name<String> nextName) {
		this.memberNamesSource.addName(nextName);
	}

	/**
	 * Add a name to the list of available names for a given attachment.
	 *
	 * @param attachmentId
	 *            the name of the attachment.
	 * @param nextName
	 *            the member name.
	 */
	public void addAttachmentName(String attachmentId, Name<String> nextName) {
		nameSource(attachmentId).addName(nextName);
	}

	/**
	 * Return the next available resource name. The name will be removed from
	 * the list of available resource names.
	 *
	 * @return the next available resource name.
	 */
	public Name<String> nextResourceName() {
		return this.resourceNamesSource.nextName();
	}

	/**
	 * Return the next available member name. The name will be removed from
	 * the list of available member names.
	 *
	 * @return the next available member name.
	 */
	public Name<String> nextMemberName() {
		return this.memberNamesSource.nextName();
	}

	/**
	 * Return the next available name for a given attachment. The name will be
	 * removed from the list of available names for the specified attachment.
	 *
	 * @param attachmentId
	 *            the name of the attachment.
	 * @return the next available name for the specified attachment.
	 */
	public Name<String> nextAttachmentName(String attachmentId) {
		NameSource result = this.attachmentNameSources.get(attachmentId);
		if(result==null) {
			result=new NameSource("attachment <<"+attachmentId+">>");
		}
		return result.nextName();
	}

	/**
	 * Create a new name provider.
	 *
	 * @param resource
	 *            the name of the owner of the name provider.
	 * @return a name provider.
	 */
	public static NameProvider create(Name<String> resource) {
		Objects.requireNonNull(resource,"Owner name cannot be null");
		return new NameProvider(resource);
	}

}


/**
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   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 static org.ldp4j.http.HttpUtils.checkToken;

import java.nio.charset.Charset;
import java.nio.charset.IllegalCharsetNameException;
import java.nio.charset.UnsupportedCharsetException;
import java.util.BitSet;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;

import com.google.common.base.MoreObjects;
import com.google.common.base.Strings;

final class ImmutableMediaType implements MediaType {

	private static class MediaRange {
		private String type;
		private String subType;
		private String suffix;
	}

	private static final char SLASH = '\\';

	private static final BitSet QDTEXT;
	private static final BitSet QUOTED_PAIR;

	private static final String TYPE_SEPARATOR  = "/";
	private static final String PARAM_SEPARATOR = ";";

	static {
		QDTEXT=new BitSet(0xFF);
		QDTEXT.set('\t');
		QDTEXT.set(0x20,0x7F); // All printable ASCII chars...
		QDTEXT.clear(0x5C);    // ... except '\'
		QDTEXT.set(0x80,0xFF); // Other non-ASCII chars in
		QDTEXT.set(0xFF);      // a byte

		QUOTED_PAIR=new BitSet(0xFF);
		QUOTED_PAIR.set('\t');
		QUOTED_PAIR.set(0x20,0x7F); // All printable ASCII chars
		QUOTED_PAIR.set(0x80,0xFF); // Other non-ASCII chars in
		QUOTED_PAIR.set(0xFF);      // a byte
	}

	private final String type;
	private final String subtype;
	private final String suffix;

	private final Map<String, String> parameters;
	private final Charset charset;

	ImmutableMediaType(final MediaRangeSyntax syntax, final String type, final String subtype, String suffix, final Map<String, String> parameters) {
		checkNotNull(syntax,"Syntax cannot be null");
		this.type=syntax.checkType(type);
		this.subtype=syntax.checkSubType(subtype);
		this.suffix=syntax.checkSuffix(suffix);
		ensureValidMediaType(this.type,this.subtype,this.suffix);
		this.parameters=verifyParameters(parameters);
		this.charset=getCharset(this.parameters);
	}

	@Override
	public String type() {
		return this.type;
	}

	@Override
	public String subType() {
		return this.subtype;
	}

	@Override
	public Charset charset() {
		return this.charset;
	}

	@Override
	public String suffix() {
		return this.suffix;
	}

	@Override
	public Map<String,String> parameters() {
		return this.parameters;
	}

	@Override
	public boolean isWildcard() {
		return MediaTypes.WILDCARD_TYPE.equals(this.type) || MediaTypes.WILDCARD_TYPE.equals(this.subtype);
	}

	@Override
	public String toHeader() {
		return MediaTypes.toHeader(this);
	}

	@Override
	public int hashCode() {
		return
			mediaRangeHashCode() ^
			standardParametersHashCode() ^
			customParametersHashCode();
	}

	@Override
	public boolean equals(final Object other) {
		if(this==other) {
			return true;
		}
		if(!(other instanceof ImmutableMediaType)) {
			return false;
		}
		final ImmutableMediaType that = (ImmutableMediaType) other;
		return
			hasSameMediaRange(that) &&
			hasSameStandardParameters(that) &&
			hasSameCustomParameters(that);
	}

	@Override
	public String toString() {
		return
			MoreObjects.
				toStringHelper(getClass()).
					omitNullValues().
					add("type",this.type).
					add("subtype",this.subtype).
					add("suffix",this.suffix).
					add("parameters",this.parameters).
					toString();
	}

	private int mediaRangeHashCode() {
		return
			17*
			caseInsensitiveHashCode(this.type)*
			caseInsensitiveHashCode(this.subtype)*
			(this.suffix==null?13:caseInsensitiveHashCode(this.suffix));
	}

	private int standardParametersHashCode() {
		return 13*Objects.hash(this.charset);
	}

	private int customParametersHashCode() {
		int hash=19;
		for(final Entry<String, String> parameter:this.parameters.entrySet()) {
			final String key=parameter.getKey();
			if(MediaTypes.isStandardParameter(key)) {
				continue;
			}
			hash*=caseInsensitiveHashCode(key)^parameter.getValue().hashCode();
		}
		return hash;
	}

	private boolean hasSameMediaRange(final ImmutableMediaType that) {
		return
			this.type.equalsIgnoreCase(that.type) &&
			this.subtype.equalsIgnoreCase(that.subtype) &&
			Objects.equals(this.suffix,that.suffix);
	}

	private boolean hasSameStandardParameters(final ImmutableMediaType that) {
		return Objects.equals(this.charset,that.charset);
	}

	/**
	 * Determine if the parameters in this {@code MediaType} and the supplied
	 * {@code MediaType} are equal, ignoring parameters with special semantics
	 * (Charset and Q).
	 */
	private boolean hasSameCustomParameters(final ImmutableMediaType that) {
		if(this.parameters.size()!=that.parameters.size()) {
			return false;
		}
		for(final Entry<String, String> parameter:this.parameters.entrySet()) {
			final String key=parameter.getKey();
			if(MediaTypes.isStandardParameter(key)) {
				continue;
			}

			if(!Objects.equals(parameter.getValue(),that.parameters.get(key))) {
				return false;
			}
		}
		return true;
	}

	/**
	 * Parse the given String into a single {@code MediaType}.
	 *
	 * @param mediaType
	 *            the string to parse
	 * @param syntax the syntax of the media type
	 * @return the mime type
	 * @throws InvalidMediaTypeException
	 *             if the string cannot be parsed
	 */
	static ImmutableMediaType fromString(final String mediaType, final MediaRangeSyntax syntax) {
		if(mediaType==null) {
			throw new InvalidMediaTypeException("Media type cannot be null");
		}
		if(mediaType.isEmpty()) {
			throw new InvalidMediaTypeException(mediaType,"Media type cannot be empty");
		}
		final String[] parts = mediaType.split(PARAM_SEPARATOR);

		String fullType = parts[0];
		// java.net.HttpURLConnection returns a *; q=.2 Accept header
		if(MediaTypes.WILDCARD_TYPE.equals(fullType)) {
			fullType = "*/*";
		}
		final MediaRange mr=parseMediaRange(mediaType,HttpUtils.trimWhitespace(fullType));
		final Map<String,String> parameters=parseParameters(mediaType,parts);
		try {
			return new ImmutableMediaType(syntax,mr.type,mr.subType,mr.suffix,parameters);
		} catch (final IllegalArgumentException ex) {
			throw new InvalidMediaTypeException(mediaType,ex,"Could not create media type");
		}
	}

	static ImmutableMediaType copyOf(final MediaType other) {
		ImmutableMediaType result=null;
		if(other instanceof ImmutableMediaType) {
			result=(ImmutableMediaType)other;
		} else if(other!=null) {
			final String header = other.toHeader();
			try {
				result=fromString(header,MediaRangeSyntax.RFC6838);
			} catch (InvalidMediaTypeException e) { // NOSONAR
				result=fromString(header,MediaRangeSyntax.RFC7230);
			}
		}
		return result;
	}

	private static int caseInsensitiveHashCode(String str) {
		return str.toLowerCase(Locale.ENGLISH).hashCode();
	}

	private static MediaRange parseMediaRange(final String mediaType, final String fullType) {
		if(fullType.isEmpty()) {
			throw new InvalidMediaTypeException(mediaType,"No media range specified");
		}
		final MediaRange mr=new MediaRange();
		parseTypes(mr,mediaType,fullType);
		parseSuffix(mr,mediaType);
		return mr;
	}

	/**
	 * As per RFC 6838, Section 4.2 structuring syntaxes specifier syntaxes are
	 * defined after the last '+' symbol.
	 *
	 * @see [RFC 6838]
	 *      Media Type Specifications and Registration Procedures, Section
	 *      4.2
	 */
	private static void parseSuffix(final MediaRange mr, final String mediaType) {
		final String subType=mr.subType;
		final int plusIdx=subType.lastIndexOf('+');
		if(plusIdx==0) {
			throw new InvalidMediaTypeException(mediaType,"missing subtype for structured media type ("+subType.substring(1)+")");
		} else if(plusIdx==subType.length()-1) {
			throw new InvalidMediaTypeException(mediaType,"missing suffix for structured media type ("+subType.substring(0,subType.length()-1)+")");
		} else if(plusIdx>0) {
			mr.subType=subType.substring(0,plusIdx);
			mr.suffix=subType.substring(plusIdx+1);
		} // Otherwise the subtype does not define a structuring syntax.
	}

	private static void parseTypes(final MediaRange mr, final String mediaType, final String fullType) {
		final int separatorIdx=fullType.indexOf('/');
		if(separatorIdx==-1) {
			throw new InvalidMediaTypeException(mediaType,"No media range subtype specified");
		} else if(separatorIdx==0) {
			throw new InvalidMediaTypeException(mediaType,"No media range type specified");
		} else if(separatorIdx==fullType.length()-1) {
			throw new InvalidMediaTypeException(mediaType,"No media range subtype specified");
		}
		final String[] types=fullType.split(TYPE_SEPARATOR);
		if(types.length!=2) {
			throw new InvalidMediaTypeException(mediaType,"Expected 2 types in media range but got "+types.length+"");
		}
		mr.type=types[0];
		mr.subType=types[1];
	}

	private static Map<String, String> parseParameters(final String mediaType, final String[] parts) {
		Map<String, String> parameters=Collections.emptyMap();
		if(parts.length>1) {
			parameters=new LinkedHashMap<>(parts.length-1);
			for(int i=1;i<parts.length;i++) {
				parseParameter(mediaType,parameters,parts[i]);
			}
		}
		return parameters;
	}

	private static void parseParameter(final String mediaType,Map<String,String> parameters,String rawParameter) {
		final String parameter=HttpUtils.trimWhitespace(rawParameter);
		final int eqIndex=parameter.indexOf('=');
		if(eqIndex==-1) {
			throw new InvalidMediaTypeException(mediaType,"Invalid parameter '"+rawParameter+"': no value defined");
		}
		final String name=parameter.substring(0,eqIndex);
		final String value=parameter.substring(eqIndex+1);
		final String previous=parameters.put(name, value);
		if(previous!=null && !areCompatible(name, previous, value)) {
			throw new InvalidMediaTypeException(mediaType,"Duplicated parameter '"+name+"': found '"+value+"' after '"+previous+"'");
		}
	}

	private static boolean areCompatible(final String attribute, final String first, final String second) {
		String actualFirst=first;
		String actualSecond=second;
		boolean caseSensitive=true;
		if(MediaTypes.PARAM_CHARSET.equalsIgnoreCase(attribute)) {
			actualFirst=HttpUtils.unquote(first);
			actualSecond=HttpUtils.unquote(second);
			caseSensitive=false;
		}
		if(caseSensitive) {
			return actualFirst.equals(actualSecond);
		} else {
			return actualFirst.equalsIgnoreCase(actualSecond);
		}
	}

	private static Charset getCharset(final Map<String, String> parameters) {
		Charset result=null;
		final String charsetName = parameters.get(MediaTypes.PARAM_CHARSET);
		if(charsetName!=null) {
			result = Charset.forName(HttpUtils.unquote(charsetName));
		}
		return result;
	}

	private static void ensureValidMediaType(final String type, final String subtype, String suffix) {
		if(MediaTypes.WILDCARD_TYPE.equals(type)) {
			checkArgument(MediaTypes.WILDCARD_TYPE.equals(subtype),"Wildcard type is legal only in wildcard media range ('*/*')");
			checkArgument(suffix==null,"Wildcard structured syntax media types are not allowed (i.e., '*/*+%s')",suffix);
		} else {
			checkArgument(!"*".equals(suffix),"Structured syntax suffix cannot be a wildcard");
		}
	}

	private static Map<String, String> verifyParameters(final Map<String, String> parameters) {
		Map<String,String> tmp=Collections.emptyMap();
		if(!MoreCollections.isEmpty(parameters)) {
			final Map<String, String> map=new CaseInsensitiveMap<String>(parameters.size(),Locale.ENGLISH);
			for(final Entry<String, String> entry:parameters.entrySet()) {
				final String attribute = entry.getKey();
				final String value = entry.getValue();
				checkParameter(attribute, value);
				map.put(attribute, value);
			}
			tmp=Collections.unmodifiableMap(map);
		}
		return tmp;
	}

	private static void checkHasLength(final String argument, final String message, Object... args) {
		checkArgument(!Strings.isNullOrEmpty(argument),message,args);
	}

	/**
	 * Checks the given quoted string for illegal characters, as defined in RFC
	 * 7230, section 3.2.6.
	 *
	 * @throws IllegalArgumentException
	 *             in case of illegal characters
	 * @see Hypertext
	 *      Transfer Protocol (HTTP/1.1): Message Syntax and Routing, Section
	 *      3.2.6
	 */
	private static void checkQuotedString(final String quotedString) {
		boolean quotedPair=false;
		for(int i=0;i<quotedString.length();i++) {
			final char ch=quotedString.charAt(i);
			if(quotedPair) {
				checkArgument(QUOTED_PAIR.get(ch),"Invalid quoted-pair character '%s' in quoted string '%s' at %d",ch,quotedString ,i);
				quotedPair=false;
			} else if(ch==SLASH) {
				quotedPair=true;
			} else {
				checkArgument(QDTEXT.get(ch),"Invalid character '%s' in quoted string '%s' at %d",ch,quotedString ,i);
			}

		}
		checkArgument(!quotedPair,"Missing quoted-pair character in quoted string '%s' at %d",quotedString,quotedString.length());
	}

	private static void checkParameter(final String parameter, String value) {
		checkHasLength(parameter, "Parameter name cannot be empty");
		checkToken(parameter,"Invalid parameter name '%s'",parameter);
		checkHasLength(value, "Value for parameter '%s' cannot be empty",parameter);
		if(MediaTypes.PARAM_CHARSET.equalsIgnoreCase(parameter)) {
			final String unquotedValue = HttpUtils.unquote(value);
			try {
				Charset.forName(unquotedValue);
			} catch (final UnsupportedCharsetException ex) {
				throw new IllegalArgumentException("Unsupported charset '"+ex.getCharsetName()+"'",ex);
			} catch (final IllegalCharsetNameException ex) {
				throw new IllegalArgumentException("Invalid charset name '"+ex.getCharsetName()+"'",ex);
			}
		} else if(!HttpUtils.isQuotedString(value)) {
			checkToken(value);
		} else {
			checkQuotedString(HttpUtils.unquote(value));
		}
	}

}


/**
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   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-core:0.2.2
 *   Bundle      : ldp4j-application-kernel-core-0.2.2.jar
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 */
package org.ldp4j.application.kernel.engine;

import java.util.Date;

import org.ldp4j.application.engine.context.Capabilities;
import org.ldp4j.application.engine.context.EntityTag;
import org.ldp4j.application.engine.context.PublicEndpoint;
import org.ldp4j.application.kernel.endpoint.Endpoint;
import org.ldp4j.application.kernel.resource.Resource;
import org.ldp4j.application.kernel.resource.ResourceId;
import org.ldp4j.application.kernel.template.ResourceTemplate;

abstract class DefaultPublicEndpoint implements PublicEndpoint {

	private final Endpoint endpoint;
	private final DefaultApplicationContext applicationContext;
	private final DefaultPublicResourceFactory factory;

	private ResourceTemplate template;
	private Resource resource;
	private Capabilities capabilities;

	protected DefaultPublicEndpoint(DefaultApplicationContext applicationContext, Endpoint endpoint) {
		this.applicationContext = applicationContext;
		this.endpoint = endpoint;
		this.factory = DefaultPublicResourceFactory.newInstance(applicationContext);
	}

	protected final boolean isRoot() {
		return resolveAs(Resource.class).isRoot();
	}

	protected final DefaultPublicResource parent() {
		return createResource(resolveAs(Resource.class).parentId());
	}

	protected final Endpoint endpoint() {
		return this.endpoint;
	}

	protected final DefaultApplicationContext applicationContext() {
		return this.applicationContext;
	}

	protected final DefaultPublicResource createResource(ResourceId resourceId) {
		return this.factory.createResource(this.applicationContext.resolveResource(resourceId));
	}

	protected final ResourceTemplate template() {
		if(this.template==null) {
			this.template = this.applicationContext.resourceTemplate(resolveAs(Resource.class));
			if(this.template==null) {
				throw new IllegalStateException("Could not find template for resource "+resource.id());
			}
		}
		return this.template;
	}

	protected final <T extends Resource> T resolveAs(Class<? extends T> clazz) {
		if(this.resource==null) {
			this.resource = this.applicationContext.resolveResource(this.endpoint);
			if(this.resource==null) {
				throw new IllegalStateException("Expected resource at "+endpoint().path());
			}
		}
		if(!clazz.isInstance(this.resource)) {
			throw new IllegalStateException("Expected resource "+this.resource.id()+" to be an instance of "+clazz.getCanonicalName());
		}
		return clazz.cast(this.resource);
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public final Status status() {
		Status result=Status.PUBLISHED;
		if(this.endpoint.deleted()!=null) {
			return Status.GONE;
		}
		return result;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public final String path() {
		return this.endpoint.path();
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public final EntityTag entityTag() {
		return this.endpoint.entityTag();
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public final Date lastModified() {
		return new Date(endpoint.lastModified().getTime());
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public final Capabilities capabilities() {
		if(this.capabilities==null) {
			this.capabilities = this.applicationContext.endpointCapabilities(this.endpoint);
		}
		return this.capabilities;
	}

}


/**
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   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;

import java.util.Iterator;
import java.util.NoSuchElementException;

import org.ldp4j.util.CharSequences;

/**
 * An iterator over a {@code CharSequence} that allows receiving the sequence of
 * Unicode code points that make up the {@code CharSequence}.
 */
final class CodePointIterator implements Iterator<Integer> {

	private final CharSequence s;
	private final int length;
	private int next;
	private int index;

	CodePointIterator(final CharSequence s) {
		this.s=s;
		this.index=-1;
		this.next=0;
		if(!CharSequences.isNullOrEmpty(s)) {
			this.length=s.length();
		} else {
			this.length=0;
		}
	}

	/**
	 * Returns {@code true} if the iteration has more code points. (In other
	 * words, returns {@code true} if {@link #next} would return a code point
	 * rather than throwing an exception.)
	 *
	 * @return {@code true} if the iteration has more code points
	 */
	@Override
	public boolean hasNext() {
		return this.next < this.length;
	}

	/**
	 * Returns the next code point in the iteration.
	 *
	 * @return the next code point in the iteration
	 * @throws NoSuchElementException
	 *             if the iteration has no more code points
	 */
	@Override
	public Integer next() {
		if(!hasNext()) {
			throw new NoSuchElementException("No more codepoints available in the CharSequence");
		}
		this.index=this.next;
		final Integer codePoint = Character.codePointAt(this.s, this.next);
		this.next+=Character.charCount(codePoint);
		return codePoint;
	}

	/**
	 * The operation is not supported by the iterator.
	 *
	 * @throws UnsupportedOperationException
	 *             always, as the {@code remove} operation is not supported by
	 *             this iterator
	 */
	@Override
	public void remove() {
		throw new UnsupportedOperationException();
	}

	int index() {
		return this.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-examples:0.2.2
 *   Bundle      : ldp4j-application-examples-0.2.2.jar
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 */
package org.ldp4j.example;

import java.net.URI;
import java.util.Date;

import org.ldp4j.application.ApplicationContext;
import org.ldp4j.application.data.DataSet;
import org.ldp4j.application.data.Individual;
import org.ldp4j.application.data.Literals;
import org.ldp4j.application.data.ManagedIndividualId;
import org.ldp4j.application.data.Name;
import org.ldp4j.application.session.ResourceSnapshot;
import org.ldp4j.application.session.WriteSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Utility class that demonstrates how to update a resource at runtime.
 */
final class DynamicResourceUpdater implements Runnable {

	static final URI REFRESHED_ON = URI.create("http://www.ldp4j.org/ns#refreshedOn");

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

	private DynamicResourceHandler handler;
	private Name<String> name;

	/**
	 * Create a new instance with a handler and a resource name.
	 *
	 * @param handler
	 *            the handler that holds the resource to be updated.
	 * @param name
	 *            the name of the resource to be updated.
	 */
	public DynamicResourceUpdater(DynamicResourceHandler handler, Name<String> name) {
		this.handler = handler;
		this.name = name;
	}

	/**
	 * Update the resource representation adding a temporal timestamp of when
	 * the resource was last updated.
	 */
	@Override
	public void run() {
		ApplicationContext ctx = ApplicationContext.getInstance();
		Date date = new Date();
		LOGGER.debug("Starting update process on {}...",date);
		try(WriteSession session = ctx.createSession()) {
			ResourceSnapshot snapshot = session.find(ResourceSnapshot.class,this.name,DynamicResourceHandler.class);
			DataSet dataSet = this.handler.get(snapshot);
			Individual<?,?> individual =
				dataSet.
					individualOfId(
						ManagedIndividualId.
							createId(this.name, DynamicResourceHandler.ID));
			individual.
				addValue(
					REFRESHED_ON,
					Literals.of(date).dateTime());
			this.handler.update(this.name, dataSet);
			session.modify(snapshot);
			session.saveChanges();
		} catch (Exception e) {
			LOGGER.error("Could not update resource",e);
		} finally {
			LOGGER.debug("Finalized update process");
		}
	}

}