/**
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   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 static com.google.common.base.Preconditions.checkState;

import java.net.URI;
import java.util.Collections;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicLong;

import org.ldp4j.application.data.constraints.Constraints;
import org.ldp4j.application.engine.context.HttpRequest;
import org.ldp4j.application.kernel.constraints.ConstraintReport;
import org.ldp4j.application.kernel.constraints.ConstraintReportId;
import org.ldp4j.application.kernel.resource.Attachment;
import org.ldp4j.application.kernel.resource.Resource;
import org.ldp4j.application.kernel.resource.ResourceId;
import org.ldp4j.application.kernel.resource.ResourceVisitor;
import org.ldp4j.application.kernel.resource.Resources;
import org.ldp4j.application.kernel.template.ResourceTemplate;
import org.ldp4j.application.kernel.template.TemplateIntrospector;

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

class InMemoryResource extends AbstractInMemoryResource implements Resource {

	private static final class VersionGenerator {

		private final ConcurrentMap<String,AtomicLong> attachmentCounter=new ConcurrentHashMap<String, AtomicLong>();

		long nextVersion(String id) {
			AtomicLong counter=this.attachmentCounter.putIfAbsent(id, new AtomicLong(-1));
			if(counter==null) {
				counter=this.attachmentCounter.get(id);
			}
			return counter.incrementAndGet();
		}

	}

	private static final class InMemoryAttachment implements Attachment {

		private final AttachmentId attachmentId;
		private final long version;

		private InMemoryAttachment(AttachmentId attachmentId, long version) {
			this.attachmentId = attachmentId;
			this.version = version;
		}

		private AttachmentId attachmentId() {
			return this.attachmentId;
		}

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

		@Override
		public ResourceId resourceId() {
			return this.attachmentId.resourceId();
		}

		@Override
		public long version() {
			return this.version;
		}

		@Override
		public String toString() {
			return
				MoreObjects.
					toStringHelper(getClass()).
						omitNullValues().
						add("id", id()).
						add("resourceId", resourceId()).
						add("version", this.version).
						toString();
		}

	}

	private final Map<AttachmentId, InMemoryAttachment> attachments;
	private final Map<String, AttachmentId> attachmentsById;
	private final Map<ResourceId, AttachmentId> attachmentsByResourceId;
	private final VersionGenerator versionGenerator;

	private final Map<String,ConstraintReportId> reports;

	private final ResourceId id;
	private final ResourceId parentId;

	private URI indirectId;

	protected InMemoryResource(ResourceId id, ResourceId parentId) {
		this.id=id;
		this.parentId = parentId;
		this.attachments=new LinkedHashMap<AttachmentId, InMemoryAttachment>();
		this.attachmentsById=new LinkedHashMap<String,AttachmentId>();
		this.attachmentsByResourceId=new LinkedHashMap<ResourceId,AttachmentId>();
		this.versionGenerator = new VersionGenerator();
		this.reports=Maps.newLinkedHashMap();
	}

	protected InMemoryResource(ResourceId id) {
		this(id,null);
	}

	protected final InMemoryResource createChild(ResourceId resourceId, ResourceTemplate template) {
		InMemoryResource newResource=null;
		if(!TemplateIntrospector.newInstance(template).isContainer()) {
			newResource=new InMemoryResource(resourceId,this.id);
		} else {
			newResource=new InMemoryContainer(resourceId,this.id);
		}
		newResource.setTemplateLibrary(getTemplateLibrary());
		return newResource;
	}

	private synchronized ConstraintReportId nextConstraintReportId() {
		String failureId=null;
		do {
			failureId=UUID.randomUUID().toString();
		} while(this.reports.containsKey(failureId));
		ConstraintReportId reportId=ConstraintReportId.create(this.id, failureId);
		this.reports.put(failureId, reportId);
		return reportId;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public ResourceId id() {
		return this.id;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void setIndirectId(URI indirectId) {
		this.indirectId=indirectId;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public URI indirectId() {
		return this.indirectId;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public boolean isRoot() {
		return this.parentId==null;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public ResourceId parentId() {
		return this.parentId;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public Attachment findAttachment(ResourceId resourceId) {
		checkNotNull(resourceId,"Attached resource identifier cannot be null");
		return this.attachments.get(this.attachmentsByResourceId.get(resourceId));
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public Resource attach(String attachmentId, ResourceId resourceId) {
		return attach(attachmentId,resourceId,Resource.class);
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public <T extends Resource> T attach(String attachmentId, ResourceId resourceId, Class<? extends T> clazz) {
		checkNotNull(attachmentId,"Attachment identifier cannot be null");
		checkNotNull(resourceId,"Attached resource identifier cannot be null");
		checkNotNull(clazz,"Attached resource class cannot be null");
		AttachmentId aId = AttachmentId.createId(attachmentId,resourceId);
		checkState(!attachments.containsKey(aId),"Resource '%s' is already attached as '%s'",resourceId,attachmentId);
		checkState(!attachmentsById.containsKey(attachmentId),"A resource is already attached as '%s'",attachmentId);
		checkState(!attachmentsByResourceId.containsKey(resourceId),"Resource '%s' is already attached",resourceId);
		ResourceTemplate attachmentTemplate=super.getTemplate(resourceId);
		checkState(Resources.areCompatible(clazz,attachmentTemplate),"Attachment '%s' is not of type '%s' (%s)",attachmentId,clazz.getCanonicalName(),attachmentTemplate.getClass().getCanonicalName());
		InMemoryResource newResource=createChild(resourceId,attachmentTemplate);
		InMemoryAttachment newAttachment = new InMemoryAttachment(aId,this.versionGenerator.nextVersion(attachmentId));
		attachments.put(newAttachment.attachmentId(),newAttachment);
		attachmentsById.put(aId.id(),aId);
		attachmentsByResourceId.put(aId.resourceId(),aId);
		return clazz.cast(newResource);
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public boolean detach(Attachment attachment) {
		boolean found=this.attachments.containsValue(attachment);
		if(found) {
			AttachmentId remove = this.attachmentsById.remove(attachment.id());
			this.attachmentsByResourceId.remove(attachment.resourceId());
			this.attachments.remove(remove);
		}
		return found;
	}

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

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void accept(ResourceVisitor visitor) {
		visitor.visitResource(this);
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public ConstraintReport addConstraintReport(Constraints constraints, Date date, HttpRequest request) {
		ConstraintReportId reportId = nextConstraintReportId();
		return new InMemoryConstraintReport(reportId,date, request, constraints);
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public Set<ConstraintReportId> constraintReports() {
		Set<ConstraintReportId> currentReports=null;
		synchronized(this) {
			currentReports=ImmutableSet.copyOf(this.reports.values());
		}
		return currentReports;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void removeFailure(ConstraintReport report) {
		if(report!=null) {
			String failureId = report.id().failureId();
			synchronized(this) {
				this.reports.remove(failureId);
			}
		}
	}

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

	@Override
	protected ToStringHelper stringHelper() {
		return
			super.stringHelper().
				add("id",this.id).
				add("parentId",this.parentId).
				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-kernel-jpa:0.2.2
 *   Bundle      : ldp4j-application-kernel-jpa-0.2.2.jar
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 */
package org.ldp4j.application.kernel.persistence.jpa;

import java.util.Date;

import javax.persistence.AttributeConverter;

/**
 * Utility class to enable persisting {@code java.util.Date} objects as
 * {@code java.lang.String} objects.
 *
 * @author Miguel Esteban Gutiérrez
 */
public final class DateConverter implements AttributeConverter<Date,String> {

	/**
	 * {@inheritDoc}
	 */
	@Override
	public String convertToDatabaseColumn(Date attribute) {
		if(attribute==null) {
			return null;
		}
		return Long.toString(attribute.getTime());
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public Date convertToEntityAttribute(String dbData) {
		if(dbData==null) {
			return null;
		}
		return new Date(Long.parseLong(dbData));
	}

}


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

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

import java.util.Date;

import org.ldp4j.application.engine.context.EntityTag;
import org.ldp4j.application.engine.util.ListenerManager;
import org.ldp4j.application.engine.util.Notification;
import org.ldp4j.application.kernel.endpoint.Endpoint;
import org.ldp4j.application.kernel.endpoint.EndpointRepository;
import org.ldp4j.application.kernel.resource.Attachment;
import org.ldp4j.application.kernel.resource.Container;
import org.ldp4j.application.kernel.resource.Member;
import org.ldp4j.application.kernel.resource.Resource;
import org.ldp4j.application.kernel.resource.ResourceId;
import org.ldp4j.application.kernel.resource.ResourceRepository;
import org.ldp4j.application.kernel.resource.Slug;
import org.ldp4j.application.kernel.service.Service;
import org.ldp4j.application.kernel.service.ServiceBuilder;
import org.ldp4j.application.kernel.spi.ModelFactory;
import org.ldp4j.application.kernel.spi.RuntimeDelegate;
import org.ldp4j.application.kernel.template.AttachedTemplate;
import org.ldp4j.application.kernel.template.ContainerTemplate;
import org.ldp4j.application.kernel.template.ResourceTemplate;
import org.ldp4j.application.kernel.template.TemplateManagementService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public final class EndpointManagementService implements Service {

	private static final class EndpointCreationNotification implements Notification<EndpointLifecycleListener> {
		private final Endpoint endpoint;

		private EndpointCreationNotification(Endpoint endpoint) {
			this.endpoint = endpoint;
		}

		@Override
		public void propagate(EndpointLifecycleListener listener) {
			listener.endpointCreated(endpoint);
		}
	}

	private static final class EndpointDeletionNotification implements Notification<EndpointLifecycleListener> {
		private final Endpoint endpoint;

		private EndpointDeletionNotification(Endpoint endpoint) {
			this.endpoint = endpoint;
		}

		@Override
		public void propagate(EndpointLifecycleListener listener) {
			listener.endpointDeleted(endpoint);
		}
	}

	private static final class EndpointManagementServiceBuilder extends ServiceBuilder<EndpointManagementService> {

		private EndpointManagementServiceBuilder() {
			super(EndpointManagementService.class);
		}

		@Override
		public EndpointManagementService build() {
			return
				new EndpointManagementService(
					super.service(TemplateManagementService.class));
		}

	}

	private static final String RESOURCE_IDENTIFIER_CANNOT_BE_NULL = "Resource identifier cannot be null";
	private static final String PATH_CANNOT_BE_NULL                = "Path cannot be null";
	private static final String LAST_MODIFIED_CANNOT_BE_NULL       = "Last modified cannot be null";
	private static final String ENTITY_TAG_CANNOT_BE_NULL          = "Entity tag cannot be null";
	private static final String RESOURCE_CANNOT_BE_NULL            = "Resource cannot be null";

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

	// TODO: Make this configurable, or subject to a policy
	private static final int MAX_ENDPOINT_CREATION_FAILURE = 3;

	private final TemplateManagementService templateManagementService;
	private final ListenerManager<EndpointLifecycleListener> listenerManager;

	private final ModelFactory modelFactory;
	private final EndpointRepository endpointRepository;
	private final ResourceRepository resourceRepository;

	private EndpointManagementService(TemplateManagementService templateManagementService) {
		this.templateManagementService = templateManagementService;
		this.modelFactory=RuntimeDelegate.getInstance().getModelFactory();
		this.endpointRepository=RuntimeDelegate.getInstance().getEndpointRepository();
		this.resourceRepository=RuntimeDelegate.getInstance().getResourceRepository();
		this.listenerManager=ListenerManager.<EndpointLifecycleListener>newInstance();
	}

	private String calculateResourcePath(Resource resource, String desiredPath) throws EndpointNotFoundException {
		if(resource.isRoot()) {
			throw new IllegalStateException("Cannot get path for root resource");
		}
		Resource parent=this.resourceRepository.resourceById(resource.parentId(),Resource.class);
		if(parent==null) {
			throw new IllegalStateException("Could not load resource '"+resource.parentId()+"' from the repository");
		}

		String result=
			parent instanceof Container?
				generatePathForMember(resource,(Container)parent,desiredPath):
				null;
		if(result==null) {
			result = generatePathForAttachment(resource,parent);
			if(result==null) {
				throw new IllegalStateException("Could not determine path for resource '"+resource.id()+"' with parent '"+parent.id()+"'");
			}

		}
		return result;
	}

	private String generatePathForAttachment(Resource child, Resource parent) throws EndpointNotFoundException {
		Attachment attachment=parent.findAttachment(child.id());
		if(attachment==null) {
			return null;
		}
		Endpoint endpoint=getResourceEndpoint(parent.id());
		ResourceTemplate parentTemplate=this.templateManagementService.templateOfId(parent.id().templateId());
		AttachedTemplate attachedTemplate = parentTemplate.attachedTemplate(attachment.id());
		return
			PathBuilder.
				create().
					addSegment(endpoint.path()).
					addSegment(attachedTemplate.path()).
					addSegment(attachment.version()>0?attachment.version():null).
					build();
	}

	private String generatePathForMember(Resource child, Container parent, String desiredPath) throws EndpointNotFoundException {
		Member member = parent.findMember(child.id());
		if(member!=null) {
			Endpoint endpoint=getResourceEndpoint(parent.id());
			ContainerTemplate parentTemplate=this.templateManagementService.templateOfId(parent.id().templateId(),ContainerTemplate.class);
			if(parentTemplate==null) {
				throw new IllegalStateException("Could not find template resource '"+parent+"'");
			}
			String slugPath=getSlugPath(parent, desiredPath);
			return
				PathBuilder.
					create().
						addSegment(endpoint.path()).
						addSegment(parentTemplate.memberPath().or("")).
						addSegment(member.number()).
						addSegment(slugPath).
						build();
		}
		return null;
	}

	private String getSlugPath(Container parent, String desiredPath) {
		String slugPath=null;
		if(desiredPath!=null) {
			Slug slug=parent.findSlug(desiredPath);
			if(slug==null) {
				slug=parent.addSlug(desiredPath);
			}
			slugPath=slug.nextPath();
		}
		return slugPath;
	}

	private Endpoint createEndpoint(Resource resource, String relativePath, EntityTag entityTag, Date lastModified) throws EndpointCreationException {
		String candidatePath=relativePath;
		int repetitions=0;
		while(repetitions<MAX_ENDPOINT_CREATION_FAILURE) {
			LOGGER.debug("({}) Creating endpoint for {} [{},{},{}]",repetitions,resource.id(),entityTag,lastModified,relativePath);
			try {
				String resourcePath = calculateResourcePath(resource,candidatePath);
				LOGGER.debug("({}) Trying resource path {} ",repetitions,resourcePath);
				Endpoint newEndpoint = this.modelFactory.createEndpoint(resourcePath, resource, lastModified, entityTag);
				this.endpointRepository.add(newEndpoint);
				return newEndpoint;
			} catch (EndpointNotFoundException e) {
				throw new EndpointCreationException("Could not calculate path for resource '"+resource.id()+"'",e);
			} catch (IllegalArgumentException e) {
				LOGGER.debug("Could not create endpoint ({} --> {})",repetitions,candidatePath,e);
				repetitions++;
				candidatePath=null;
			}
		}
		throw new EndpointCreationException("Could not create endpoint for resource '"+resource.id()+"' after "+MAX_ENDPOINT_CREATION_FAILURE+" tries");
	}

	/**
	 * Enforce http://tools.ietf.org/html/rfc7232#section-2.2:
	 * if the clock in the request is ahead of the clock of the origin
	 * server (e.g., I request from Spain the update of a resource held in USA)
	 * the last-modified data should be changed to that of the request and not
	 * a generated date from the origin server
	 */
	private Date getModificationDate(Endpoint endpoint, Date modificationDate) {
		Date result=endpoint.lastModified();
		if(modificationDate.after(result)) {
			result=modificationDate;
		}
		return result;
	}

	public void registerEndpointLifecycleListener(EndpointLifecycleListener listener) {
		this.listenerManager.registerListener(listener);
	}

	public void deregisterEndpointLifecycleListener(EndpointLifecycleListener listener) {
		this.listenerManager.deregisterListener(listener);
	}

	public Endpoint getResourceEndpoint(ResourceId resourceId) throws EndpointNotFoundException {
		checkNotNull(resourceId,RESOURCE_IDENTIFIER_CANNOT_BE_NULL);
		Endpoint endpoint = this.endpointRepository.endpointOfResource(resourceId);
		if(endpoint==null) {
			throw new EndpointNotFoundException(resourceId);
		}
		return endpoint;
	}

	public Endpoint resolveEndpoint(String path) {
		checkNotNull(path,PATH_CANNOT_BE_NULL);
		return this.endpointRepository.endpointOfPath(path);
	}

	public Endpoint createEndpointForResource(Resource resource, String relativePath, EntityTag entityTag, Date lastModified) throws EndpointCreationException {
		checkNotNull(resource,RESOURCE_CANNOT_BE_NULL);
		checkNotNull(entityTag,ENTITY_TAG_CANNOT_BE_NULL);
		checkNotNull(lastModified,LAST_MODIFIED_CANNOT_BE_NULL);
		Endpoint newEndpoint = createEndpoint(resource, relativePath, entityTag, lastModified);
		this.listenerManager.notify(new EndpointCreationNotification(newEndpoint));
		return newEndpoint;
	}

	public Endpoint modifyResourceEndpoint(Resource resource, EntityTag entityTag, Date lastModified) throws EndpointNotFoundException {
		checkNotNull(resource,RESOURCE_CANNOT_BE_NULL);
		checkNotNull(entityTag,ENTITY_TAG_CANNOT_BE_NULL);
		checkNotNull(lastModified,LAST_MODIFIED_CANNOT_BE_NULL);
		Endpoint endpoint = this.endpointRepository.endpointOfResource(resource.id());
		if(endpoint==null) {
			throw new EndpointNotFoundException(resource.id());
		}
		endpoint.
			modify(
				entityTag,
				getModificationDate(endpoint,lastModified));
		return endpoint;
	}

	public Endpoint deleteResourceEndpoint(Resource resource, Date deletionDate) throws EndpointNotFoundException {
		checkNotNull(resource,RESOURCE_CANNOT_BE_NULL);
		Endpoint endpoint = this.endpointRepository.endpointOfResource(resource.id());
		if(endpoint==null) {
			throw new EndpointNotFoundException(resource.id());
		}
		endpoint.delete(getModificationDate(endpoint,deletionDate));
		this.listenerManager.notify(new EndpointDeletionNotification(endpoint));
		return endpoint;
	}

	public static ServiceBuilder<EndpointManagementService> serviceBuilder() {
		return new EndpointManagementServiceBuilder();
	}

	public static EndpointManagementService defaultService() {
		return serviceBuilder().build();
	}

}


/**
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   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 java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

import org.ldp4j.application.kernel.endpoint.Endpoint;
import org.ldp4j.application.kernel.endpoint.EndpointRepository;
import org.ldp4j.application.kernel.lifecycle.LifecycleException;
import org.ldp4j.application.kernel.lifecycle.Managed;
import org.ldp4j.application.kernel.resource.ResourceId;

import com.google.common.collect.Maps;

final class InMemoryEndpointRepository implements Managed, EndpointRepository {

	private final Map<String,Endpoint> endpointsByPath=Maps.newLinkedHashMap();
	private final Map<ResourceId,Endpoint> endpointsByResourceName=Maps.newLinkedHashMap();

	private final Lock readLock;
	private final Lock writeLock;

	InMemoryEndpointRepository() {
		ReadWriteLock lock=new ReentrantReadWriteLock();
		this.readLock = lock.readLock();
		this.writeLock = lock.writeLock();
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public Endpoint endpointOfPath(String path) {
		this.readLock.lock();
		try {
			return this.endpointsByPath.get(path);
		} finally {
			this.readLock.unlock();
		}
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public Endpoint endpointOfResource(ResourceId id) {
		this.readLock.lock();
		try {
			Endpoint endpoint = this.endpointsByResourceName.get(id);
			if(endpoint!=null && endpoint.deleted()!=null) {
				endpoint=null;
			}
			return endpoint;
		} finally {
			this.readLock.unlock();
		}
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void add(Endpoint endpoint) {
		this.writeLock.lock();
		try {
			if(this.endpointsByPath.containsKey(endpoint.path())) {
				throw new IllegalArgumentException("An endpoint with path '"+endpoint.path()+"' already exists");
			}
			Endpoint other=endpointOfResource(endpoint.resourceId());
			if(other!=null) {
				throw new IllegalArgumentException("An endpoint with resource name '"+endpoint.resourceId()+"' already exists ("+other+")");
			}
			this.endpointsByPath.put(endpoint.path(), endpoint);
			this.endpointsByResourceName.put(endpoint.resourceId(),endpoint);
		} finally {
			this.writeLock.unlock();
		}
	}

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

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void shutdown() throws LifecycleException {
		this.writeLock.lock();
		try {
			this.endpointsByPath.clear();
			this.endpointsByResourceName.clear();
		} finally {
			this.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-api:0.2.2
 *   Bundle      : ldp4j-application-api-0.2.2.jar
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 */
package org.ldp4j.application;

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

import java.util.concurrent.atomic.AtomicLong;

import org.ldp4j.application.data.Individual;
import org.ldp4j.application.data.Name;
import org.ldp4j.application.ext.ResourceHandler;
import org.ldp4j.application.session.ResourceSnapshot;
import org.ldp4j.application.session.SessionTerminationException;
import org.ldp4j.application.session.WriteSession;
import org.ldp4j.application.session.WriteSessionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.base.MoreObjects;

final class ContextWriteSessionState implements WriteSession {

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

	private static final AtomicLong SESSION_COUNTER=new AtomicLong();

	private final long id;
	private final WriteSession delegate;
	private final ContextWriteSessionStateListener listener;

	private boolean disposed;
	private boolean completed;

	ContextWriteSessionState(WriteSession delegate, ContextWriteSessionStateListener listener) {
		this.delegate = delegate;
		this.listener = listener;
		this.id=SESSION_COUNTER.incrementAndGet();
	}

	private void doDispose() throws SessionTerminationException {
		this.listener.onDispose(this);
		this.disposed=true;
		this.delegate.close();
	}

	private void verifyExecutability() {
		checkState(!this.disposed,"Session has already been disposed");
		checkState(!this.completed,"Session has already been completed");
	}

	long id() {
		return this.id;
	}

	synchronized void dispose() {
		LOGGER.warn("Closing session {} which was not closed by the user...",this);
		try {
			doDispose();
		} catch (SessionTerminationException e) {
			LOGGER.error("Could not close session {}",this,e);
		}
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public synchronized <S extends ResourceSnapshot> S resolve(Class<? extends S> snapshotClass, Individual<?, ?> individual) {
		verifyExecutability();
		return this.delegate.resolve(snapshotClass,individual);
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public synchronized <S extends ResourceSnapshot> S find(Class<? extends S> snapshotClass, Name<?> id, Class<? extends ResourceHandler> handlerClass) {
		verifyExecutability();
		return this.delegate.find(snapshotClass,id,handlerClass);
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public synchronized void modify(ResourceSnapshot resource) {
		verifyExecutability();
		this.delegate.modify(resource);
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public synchronized void delete(ResourceSnapshot resource) {
		verifyExecutability();
		this.delegate.delete(resource);
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public synchronized void saveChanges() throws WriteSessionException {
		verifyExecutability();
		this.delegate.saveChanges();
		this.completed=true;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public synchronized void discardChanges() throws WriteSessionException {
		verifyExecutability();
		this.delegate.discardChanges();
		this.completed=true;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public synchronized void close() throws SessionTerminationException {
		if(this.disposed) {
			return;
		}
		doDispose();
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public synchronized String toString() {
		return
			MoreObjects.
				toStringHelper(getClass()).
					add("id",this.id).
					add("completed",this.completed).
					add("disposed",this.disposed).
					add("delegate",this.delegate).
					toString();
	}

}


/**
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   This file is part of the LDP4j Project:
 *     http://www.ldp4j.org/
 *
 *   Center for Open Middleware
 *     http://www.centeropenmiddleware.com/
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   Copyright (C) 2014-2016 Center for Open Middleware.
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *             http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   Artifact    : org.ldp4j.framework:ldp4j-application-kernel-jpa:0.2.2
 *   Bundle      : ldp4j-application-kernel-jpa-0.2.2.jar
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 */
package org.ldp4j.application.kernel.persistence.jpa;

import javax.persistence.AttributeConverter;

import org.ldp4j.application.engine.context.EntityTag;

/**
 * Utility class to enable persisting
 * {@code org.ldp4j.application.engine.context.EntityTag} objects as
 * {@code java.lang.String} objects.
 *
 * @see org.ldp4j.application.engine.context.EntityTag
 * @author Miguel Esteban Gutiérrez
 */
public final class EntityTagConverter implements AttributeConverter<EntityTag,String> {

	/**
	 * {@inheritDoc}
	 */
	@Override
	public String convertToDatabaseColumn(EntityTag attribute) {
		if(attribute==null) {
			return null;
		}
		return attribute.toString();
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public EntityTag convertToEntityAttribute(String dbData) {
		if(dbData==null) {
			return null;
		}
		return EntityTag.valueOf(dbData);
	}

}


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

import java.io.IOException;
import java.util.HashSet;
import java.util.Properties;
import java.util.Set;

import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
import org.springframework.core.io.Resource;

public class MonitorizedPropertyPlaceholderConfigurer extends PropertyPlaceholderConfigurer {

	public static final String LDP4J_CONFIG_DUMP = "ldp4j.config.dump";

	public static final String LDP4J_CONFIG_MODULES = "ldp4j.config.modules";

	private ConfigurationSummary table;
	private String moduleName;

	public MonitorizedPropertyPlaceholderConfigurer() {
		table = new ConfigurationSummary("<unknown>");
	}

	public void setModuleName(String moduleName) {
		this.moduleName = moduleName;
		this.table=new ConfigurationSummary(moduleName);
	}

	public String getModuleName() {
		return moduleName;
	}

	// TODO: Enable selecting where the configuration should be logged to: System.out or a Logger
	@Override
	protected Properties mergeProperties() throws IOException {
		Properties result = super.mergeProperties();
		if(canShowConfiguration(getModuleName())) {
			table.setProperties(result);
			table.dump(System.out); // NOSONAR
		}
		return result;
	}

	private boolean canShowConfiguration(String moduleName) {
		boolean canShow=false;
		if(System.getProperty(LDP4J_CONFIG_DUMP)!=null ||
		   System.getenv().get(LDP4J_CONFIG_DUMP)!=null) {
			Set<String> modules=new HashSet<String>();
			splitModules(modules, System.getProperty(LDP4J_CONFIG_MODULES,""));
			splitModules(modules, System.getenv().get(LDP4J_CONFIG_MODULES));
			if(modules.contains(moduleName)) {
				canShow=true;
			}
		}
		return canShow;
	}

	private void splitModules(Set<String> modules, String binModules) {
		if(binModules!=null) {
			String[] rawModules = binModules.split(",");
			for(String rawModule:rawModules) {
				modules.add(rawModule.trim());
			}
		}
	}

	@Override
	public void setLocation(Resource location) {
		table.setResources(new Resource[]{location});
		super.setLocation(location);
	}

	@Override
	public void setLocations(Resource[] locations) {
		table.setResources(locations);
		super.setLocations(locations);
	}

}


/**
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   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.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URI;
import java.util.Collection;
import java.util.Objects;
import java.util.concurrent.CopyOnWriteArrayList;

import javax.xml.namespace.QName;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;

final class TypeAdapter<S,T> {

	static final class QNameAdapter {
		private QNameAdapter() {
		}
		/**
		 * Transform a term to a {@code QName}.
		 *
		 * @param term
		 *            the term to adapt.
		 * @return a {@code QName} object that represents the specified term.
		 */
		public static QName adaptTo(Term term) {
			Vocabulary vocabulary = term.getDeclaringVocabulary();
			return
				new QName(
					vocabulary.getNamespace(),
					term.entityName(),
					vocabulary.getPreferredPrefix());
		}
	}

	static final class URIAdapter {
		private URIAdapter(){
		}
		/**
		 * Transform a term to a {@code URI}.
		 *
		 * @param term
		 *            the term to adapt.
		 * @return a {@code URI} object that represents the specified term.
		 */
		public static URI adaptTo(Term term) {
			return
				URI.
					create(
						term.getDeclaringVocabulary().getNamespace()+
						term.entityName());
		}
	}

	static final String ADAPTER_NAME_CONVENTION = "adaptTo";

	static final CopyOnWriteArrayList<Class<?>> ADAPTER_CLASSES=Lists.newCopyOnWriteArrayList(); // NOSONAR

	static {
		registerAdapterClass(URIAdapter.class);
		registerAdapterClass(QNameAdapter.class);
	}

	private final Class<? extends T> resultClass;
	private final Method adapterMethod;

	private TypeAdapter(Method adapterMethod, Class<? extends T> resultClass) {
		this.adapterMethod = adapterMethod;
		this.resultClass = resultClass;
	}

	private String getAdapterFailureMessage(String messageTemplate, Object... args) {
		return String.format("Chosen adapter method '%s' ",adapterMethod).concat(String.format(messageTemplate,args));
	}

	T adapt(S value) {
		try {
			T result = resultClass.cast(adapterMethod.invoke(null, value));
			if(result==null) {
				throw new IllegalStateException(getAdapterFailureMessage("could not adapt value '%s",value));
			}
			return result;
		} catch (IllegalArgumentException e) {
			throw new IllegalStateException(getAdapterFailureMessage("should accept '%s'",value.getClass().getCanonicalName()),e);
		} catch (IllegalAccessException e) {
			throw new IllegalStateException(getAdapterFailureMessage("should be accesible"),e);
		} catch (InvocationTargetException e) {
			throw new IllegalStateException(getAdapterFailureMessage("failed while adapting value '%s'",value),e);
		}
	}

	private static Collection<Class<?>> findTypeAdapters() {
		return ImmutableList.copyOf(TypeAdapter.ADAPTER_CLASSES);
	}

	private static <S,T> TypeAdapter<S,T> doCreateAdapter(Class<? extends T> targetType, AdapterMethodValidator validator) {
		for(Class<?> termAdapter:findTypeAdapters()) {
			for(Method candidate:termAdapter.getMethods()) {
				if(validator.isValid(candidate)) {
					return new TypeAdapter<S,T>(candidate,targetType);
				}
			}
		}
		throw new CannotAdaptClassesException(
			"Could not find adapter of adapting class '" +
			validator.getTargetClass().getCanonicalName() +
			"' to '" + targetType.getCanonicalName() + "'");
	}

	/**
	 * Register a new adapter class.
	 *
	 * @param clazz
	 *            the adapter class.
	 * @throws NullPointerException
	 *             if the adapter class is {@code null}.
	 */
	static void registerAdapterClass(Class<?> clazz) {
		Objects.requireNonNull(clazz,"Adapter class cannot be null");
		TypeAdapter.ADAPTER_CLASSES.addIfAbsent(clazz);
	}

	/**
	 * Get an adapter capable of transforming instances of a source type into
	 * instances of a target type.
	 *
	 * @param sourceType
	 *            The type of the instances that the adapter should be able to
	 *            transform.
	 * @param targetType
	 *            The type of instances that the adapter should be able to
	 *            create.
	 * @return An adapter capable of transforming instances of the {@code sourceType}
	 *         into instances of the {@code targetType}.
	 * @throws CannotAdaptClassesException
	 *             when no adapter capable of carrying out such transformation
	 *             is available.
	 */
	static <S,T> TypeAdapter<S,T> createAdapter(Class<? extends S> sourceType, Class<? extends T> targetType) {
		return doCreateAdapter(targetType,AdapterMethodValidator.newInstance(targetType, sourceType));
	}

	/**
	 * Transform a given object into an instance of the specified type.
	 *
	 * @param object
	 *            The object that is to be transformed.
	 * @param resultClass
	 *            The type of instance that is to be created.
	 * @return An instance of type {@code resultClass} created upon the
	 *         specified @ object} .
	 * @throws CannotAdaptClassesException
	 *             when no adapter capable of carrying out such transformation
	 *             is available.
	 */
	static <S,T> T adapt(S object, Class<? extends T> resultClass) {
		return TypeAdapter.<S,T>doCreateAdapter(resultClass, AdapterMethodValidator.newInstance(resultClass, object)).adapt(object);
	}
}


/**
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   This file is part of the LDP4j Project:
 *     http://www.ldp4j.org/
 *
 *   Center for Open Middleware
 *     http://www.centeropenmiddleware.com/
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   Copyright (C) 2014-2016 Center for Open Middleware.
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *             http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   Artifact    : org.ldp4j.framework:ldp4j-application-data:0.2.2
 *   Bundle      : ldp4j-application-data-0.2.2.jar
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 */
package org.ldp4j.application.data;

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

import org.ldp4j.application.vocabulary.Term;

final class PropertyHelperImpl implements PropertyHelper {

	private URI propertyId;
	private Individual<?, ?> individual;

	PropertyHelperImpl(URI propertyId, Individual<?,?> individual) {
		this.propertyId = propertyId;
		this.individual = individual;
	}

	private Property getProperty() {
		return this.individual.property(propertyId);
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public <T> T firstValue(final Class<? extends T> aClazz) {
		Property property=getProperty();
		if(property==null) {
			return null;
		}
		LiteralValueExtractor<T> extractor =LiteralValueExtractor.newInstance(aClazz);
		for(Value value:property) {
			value.accept(extractor);
			if(extractor.isAvailable()) {
				break;
			}
		}
		return extractor.getValue();
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public IndividualHelper firstIndividual() {
		Property property=getProperty();
		if(property==null) {
			return null;
		}
		for(Value value:property) {
			if(value instanceof Individual<?,?>) {
				return new IndividualHelperImpl((Individual<?,?>)value);
			}
		}
		return null;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public <T extends Serializable, S extends Individual<T,S>> T firstIndividual(final Class<? extends S> clazz) {
		Property property=getProperty();
		if(property==null) {
			return null;
		}
		IndividualExtractor<T,S> extractor=IndividualExtractor.newInstance(clazz);
		for(Value value:property) {
			value.accept(extractor);
			if(extractor.isAvailable()) {
				return extractor.getValue().id();
			}
		}
		return null;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public IndividualPropertyHelper withLiteral(Object rawValue) {
		if(rawValue!=null) {
			Literal<?> value = Literals.newLiteral(rawValue);
			this.individual.addValue(this.propertyId,value);
		}
		return new IndividualPropertyHelperImpl(new IndividualHelperImpl(this.individual),this);
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public IndividualPropertyHelper withIndividual(Name<?> id) {
		if(id!=null) {
			@SuppressWarnings("rawtypes")
			Individual<?,?> srcIndividual = this.individual.dataSet().individual((Name)id, LocalIndividual.class);
			this.individual.addValue(this.propertyId,srcIndividual);
		}
		return new IndividualPropertyHelperImpl(new IndividualHelperImpl(this.individual),this);
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public IndividualPropertyHelper withIndividual(Name<?> id, String managerId) {
		if(id!=null) {
			Individual<?,?> srcIndividual =
				this.individual.
					dataSet().
						individual(
							ManagedIndividualId.createId(id, managerId),
							ManagedIndividual.class);
			this.individual.addValue(this.propertyId,srcIndividual);
		}
		return new IndividualPropertyHelperImpl(new IndividualHelperImpl(this.individual),this);
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public IndividualPropertyHelper withIndividual(Name<?> id, String managerId, URI path) {
		if(id!=null) {
			ManagedIndividualId parentId=ManagedIndividualId.createId(id,managerId);
			RelativeIndividualId individualId=RelativeIndividualId.createId(parentId, path);
			Individual<?,?> srcIndividual=
				this.individual.
					dataSet().
						individual(
							individualId,
							RelativeIndividual.class);
			this.individual.addValue(this.propertyId,srcIndividual);
		}
		return new IndividualPropertyHelperImpl(new IndividualHelperImpl(this.individual),this);
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public IndividualPropertyHelper withIndividual(Name<?> id, String managerId, String path) {
		if(id!=null) {
			return withIndividual(id, managerId, URI.create(path));
		}
		return new IndividualPropertyHelperImpl(new IndividualHelperImpl(this.individual),this);
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public IndividualPropertyHelper withIndividual(URI id) {
		if(id!=null) {
			Individual<?,?> srcIndividual=null;
			if(id.isAbsolute()) {
				srcIndividual=this.individual.dataSet().individual(id, ExternalIndividual.class);
			} else {
				srcIndividual=this.individual.dataSet().individual(id, NewIndividual.class);
			}
			this.individual.addValue(this.propertyId,srcIndividual);
		}
		return new IndividualPropertyHelperImpl(new IndividualHelperImpl(this.individual),this);
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public IndividualPropertyHelper withIndividual(String id) {
		if(id!=null) {
			withIndividual(URI.create(id));
		}
		return new IndividualPropertyHelperImpl(new IndividualHelperImpl(this.individual),this);
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public IndividualPropertyHelper withIndividual(Term id) {
		if(id!=null) {
			withIndividual(id.qualifiedEntityName());
		}
		return new IndividualPropertyHelperImpl(new IndividualHelperImpl(this.individual),this);
	}

}


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

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

import org.ldp4j.application.ApplicationApiRuntimeException;
import org.ldp4j.application.ApplicationContextException;
import org.ldp4j.application.session.ReadSession;
import org.ldp4j.application.session.ResourceSnapshot;
import org.ldp4j.application.session.WriteSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public abstract class RuntimeDelegate {

	private static final String DISABLE = "disable";

	/**
	 * Name of the system property for controlling the resolution of the
	 * {@code RuntimeDelegate} instance leveraging the
	 * {@link java.util.ServiceLoader} mechanism.
	 *
	 * The absence of this property or any value but {@value #DISABLE}, despite
	 * the case, will enable the enable the usage of this mechanism.
	 */
	public static final String APPLICATION_ENGINE_SPI_FINDER = "org.ldp4j.application.spi.finder";

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

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

	private static final String INSTANTIATE_ACTION = "instantiate";

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

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

	private static final ReflectPermission SUPPRESS_ACCESS_CHECKS_PERMISSION = new ReflectPermission("suppressAccessChecks");

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

	/**
	 * Obtain an {@code RuntimeDelegate} instance using the method described
	 * in {@link RuntimeDelegate#getInstance()}.
	 *
	 * @return an instance of {@code RuntimeDelegate} if available, or null if
	 *         a default implementation class is to be selected.
	 */
	private static RuntimeDelegate findDelegate() {
		RuntimeDelegate result=null;
		try {
			result=createRuntimeDelegateFromSPI();
			if(result==null) {
				result=createRuntimeDelegateFromConfigurationFile();
			}
			if(result==null) {
				String delegateClassName = System.getProperty(APPLICATION_ENGINE_SPI_PROPERTY);
				if(delegateClassName!=null) {
					result=createRuntimeDelegateForClassName(delegateClassName);
				}
			}
		} catch (Exception ex) {
			LOGGER.warn("Could not find application engine",ex);
		}
		return result;
	}

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

	/**
	 * Get the configuration file for the Application Engine, that is, the a
	 * file named {@value #APPLICATION_ENGINE_SPI_CFG} in the lib
	 * directory of current JAVA_HOME.
	 *
	 * @return The configuration file for the runtime instance.
	 */
	private static File getConfigurationFile() {
		return new File(new File(System.getProperty("java.home")),"lib"+File.separator+APPLICATION_ENGINE_SPI_CFG);
	}

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

	private static RuntimeDelegate createRuntimeDelegateFromSPI() {
		if(!DISABLE.equalsIgnoreCase(System.getProperty(APPLICATION_ENGINE_SPI_FINDER))) {
			Iterator<RuntimeDelegate> iterator = ServiceLoader.load(RuntimeDelegate.class).iterator();
			while(iterator.hasNext()) {
				try {
					return iterator.next();
				} catch (ServiceConfigurationError ex) {
					LOGGER.error("Could not load {} service. Full stacktrace follows.",RuntimeDelegate.class.getCanonicalName(),ex);
				}
			}
		}
		return null;
	}

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

	private static void handleFailure(String delegateClassName, String action, Exception failure) {
		LOGGER.warn("Could not {} delegate class {}",action,delegateClassName,failure);
	}

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

	 * The algorithm used to locate the {@code RuntimeDelegate} subclass to
	 * use consists of the following steps:
	 *

	 *

	 *

	 * If a resource with the name of
	 * {@code META-INF/services/}<tt>{@value #APPLICATION_ENGINE_SPI_PROPERTY}</tt>
	 * exists, then its first line, if present, is used as the UTF-8 encoded
	 * name of the implementation class.

	 *

	 * If a file named {@value #APPLICATION_ENGINE_SPI_CFG} in the
	 * lib directory of current JAVA_HOME exists and it is readable
	 * by the {@code java.util.Properties.load(InputStream)} method and it
	 * contains an entry whose key is
	 * {@value #APPLICATION_ENGINE_SPI_PROPERTY}, then the value of that entry
	 * is used as the name of the implementation class.

	 *

	 * If a system property named {@value #APPLICATION_ENGINE_SPI_PROPERTY} is
	 * defined, then its value is used as the name of the implementation class.

	 *

	 * Finally, a default implementation class name is used.

	 *

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

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

	private static SecurityManager getSecurityManager() {
		return System.getSecurityManager();
	}

	private static class DefaultRuntimeDelegate extends RuntimeDelegate {

		private static final class NullResourceSnapshotResolver implements ResourceSnapshotResolver {
			@Override
			public URI resolve(ResourceSnapshot resource) {
				return null;
			}
			@Override
			public ResourceSnapshot resolve(URI endpoint) {
				return null;
			}
		}

		@Override
		public WriteSession createSession() throws ApplicationContextException {
			throw new ApplicationContextException("No runtime delegate found");
		}

		@Override
		public boolean isOffline() {
			return true;
		}

		@Override
		public ResourceSnapshotResolver createResourceResolver(URI canonicalBase, ReadSession session) {
			return new NullResourceSnapshotResolver();
		}

		@Override
		public void registerShutdownListener(ShutdownListener listener) {
			throw new ApplicationApiRuntimeException("No runtime delegate found");
		}

	}

	/**
	 * Return if the application is offline.
	 *
	 * @return if the application is offline.
	 */
	public abstract boolean isOffline();

	/**
	 * Create a {@code WriteSession}.
	 *
	 * @return a write session.
	 * @throws ApplicationContextException
	 *             if the session could not be created.
	 */
	public abstract WriteSession createSession() throws ApplicationContextException;

	/**
	 * Create a {@code ResourceSnapshotResolver}
	 *
	 * @param canonicalBase
	 *            the canonical base URI of the application.
	 * @param session
	 *            the {@code ReadSession} that the resolver may use for finding
	 *            out the snapshots.
	 * @return a resolver.
	 */
	public abstract ResourceSnapshotResolver createResourceResolver(URI canonicalBase, ReadSession session);

	/**
	 * Register a {@code ShutdownListener}
	 *
	 * @param listener
	 *            the listener to register
	 */
	public abstract void registerShutdownListener(ShutdownListener listener);

}


/**
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   This file is part of the LDP4j Project:
 *     http://www.ldp4j.org/
 *
 *   Center for Open Middleware
 *     http://www.centeropenmiddleware.com/
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   Copyright (C) 2014-2016 Center for Open Middleware.
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *             http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   Artifact    : org.ldp4j.framework:ldp4j-application-engine-sdk:0.2.2
 *   Bundle      : ldp4j-application-engine-sdk-0.2.2.jar
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 */
package org.ldp4j.application.sdk.internal;

import org.ldp4j.application.sdk.spi.ObjectFactory;
import org.ldp4j.application.sdk.spi.ObjectParseException;

/**
 * Object factory for parsing and formatting Boolean objects.
 */
public class BooleanObjectFactory implements ObjectFactory<Boolean> {

	/**
	 * {@inheritDoc}
	 */
	@Override
	public Class<? extends Boolean> targetClass() {
		return Boolean.class;
	}

	/**
	 * Returns a {@code Boolean} with a value represented by the specified
	 * string. The {@code Boolean} returned represents:
	 *

	 *

	 * a {@code true} value if the string argument is not {@code null} and is equal,
	 * ignoring case, to the string {@code "true"}.

	 *

	 * a {@code false} value if the string argument is not {@code null} and is equal,
	 * ignoring case, to the string {@code "false"}.

	 *

	 *
	 * @param rawValue
	 *            a string.
	 * @return the {@code Boolean} value represented by the string.
	 * @throws ObjectParseException
	 *             iff the specified raw value cannot be converted to a
	 *             {@code Boolean} object.
	 */
	@Override
	public Boolean fromString(final String rawValue) {
		try {
			return PrimitiveObjectFactory.parseBoolean(rawValue);
		} catch (final IllegalArgumentException e) {
			throw new ObjectParseException(e,Boolean.class, rawValue);
		}
	}

	/**
	 * Returns a {@code String} object representing the specified boolean. If
	 * the specified boolean is {@code true}, then the string {@code "true"}
	 * will be returned, otherwise the string {@code "false"} will be returned.
	 *
	 * @param value
	 *            the boolean to be converted
	 * @return the string representation of the specified {@code boolean}
	 */
	@Override
	public String toString(final Boolean value) {
		return value.toString();
	}

}


/**
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   This file is part of the LDP4j Project:
 *     http://www.ldp4j.org/
 *
 *   Center for Open Middleware
 *     http://www.centeropenmiddleware.com/
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   Copyright (C) 2014-2016 Center for Open Middleware.
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *             http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   Artifact    : org.ldp4j.commons:ldp4j-commons-http:0.2.2
 *   Bundle      : ldp4j-commons-http-0.2.2.jar
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 */
package org.ldp4j.http;

import java.nio.charset.StandardCharsets;
import java.util.Locale;
import java.util.Objects;

import com.google.common.collect.ImmutableList;

public final class ContentNegotiator {

	static final MediaType         DEFAULT_MEDIA_TYPE         = MediaTypes.of("text","plain");
	static final Language          DEFAULT_LANGUAGE           = Languages.of(Locale.ENGLISH);
	static final CharacterEncoding DEFAULT_CHARACTER_ENCODING = CharacterEncodings.of(StandardCharsets.UTF_8);

	private final ImmutableList.Builder<MediaType> mediaTypes;
	private final ImmutableList.Builder<CharacterEncoding> characterEncodings;
	private final ImmutableList.Builder<Language> languages;
	private final ImmutableList.Builder<Alternative> supported;

	private final ImmutableList.Builder<Weighted<MediaType>> accepts;
	private final ImmutableList.Builder<Weighted<CharacterEncoding>> acceptCharsets;
	private final ImmutableList.Builder<Weighted<Language>> acceptLanguages;

	private ImmutableVariant errorVariant;

	private ContentNegotiator() {
		this.mediaTypes=ImmutableList.builder();
		this.characterEncodings=ImmutableList.builder();
		this.languages=ImmutableList.builder();
		this.supported=ImmutableList.builder();
		this.accepts=ImmutableList.builder();
		this.acceptCharsets=ImmutableList.builder();
		this.acceptLanguages=ImmutableList.builder();
		this.errorVariant=defaultErrorVariant();
	}

	private ImmutableVariant defaultErrorVariant() {
		return ImmutableVariant.
			newInstance().
				charset(DEFAULT_CHARACTER_ENCODING).
				language(DEFAULT_LANGUAGE).
				type(DEFAULT_MEDIA_TYPE);
	}

	public Variant onError() {
		return this.errorVariant;
	}

	public ContentNegotiator onError(final Variant variant) {
		if(variant!=null) {
			this.errorVariant=ImmutableVariant.copyOf(variant);
		} else {
			this.errorVariant=defaultErrorVariant();
		}
		return this;
	}

	public ContentNegotiator support(final MediaType mediaType) {
		if(mediaType!=null) {
			this.mediaTypes.add(mediaType);
		}
		return this;
	}

	public ContentNegotiator support(final CharacterEncoding characterEncoding) {
		if(characterEncoding!=null) {
			this.characterEncodings.add(characterEncoding);
		}
		return this;
	}

	public ContentNegotiator support(final Language language) {
		if(language!=null) {
			this.languages.add(language);
		}
		return this;
	}

	public ContentNegotiator support(final Alternative alternative) {
		if(alternative!=null) {
			this.supported.add(alternative);
		}
		return this;
	}

	public ContentNegotiator accept(final String header) {
		Objects.requireNonNull(header,"Accept header cannot be null");
		this.accepts.add(ContentNegotiationUtils.accept(header));
		return this;
	}

	public ContentNegotiator acceptCharset(final String header) {
		Objects.requireNonNull(header,"Accept-Charset header cannot be null");
		this.acceptCharsets.add(ContentNegotiationUtils.acceptCharset(header));
		return this;
	}

	public ContentNegotiator acceptLanguage(final String header) {
		Objects.requireNonNull(header,"Accept-Language header cannot be null");
		this.acceptLanguages.add(ContentNegotiationUtils.acceptLanguage(header));
		return this;
	}

	/**
	 * Carry out the content negotiation taking into account both the supported
	 * and the acceptable media types, character encodings, and languages.
	 *

	 * The method implements a simplified version of the Remote Variant
	 * Selection Algorithm defined in RFC 2296.
	 *

	 * The differences between the implemented algorithm and the original one
	 * are the following:
	 *

	 *
There is no support for features,
	 *
Variant source-quality is assigned linearly (with ratio
	 * -0.125) of the values suggested in Section 5.3 of RFC 2296;
	 *
The round5 function is not applied during the computation of
	 * overall quality values.
	 *

	 * The reason for this latter difference is that in the presence of
	 * fine-grained qualities (i.e., 0.001) computed quality values
	 * converge to 10-12, and can make variants with non-zero
	 * computed qualities appear like they are not amenable to selection.
	 *
	 * @return the result of the content negotiation
	 * @see RFC 2296: HTTP Remote
	 *      Variant Selection Algorithm -- RVSA/1.0
	 */
	public NegotiationResult negotiate() {
		return
			new ContentNegotiationAlgorithm(
				this.mediaTypes.build(),
				this.characterEncodings.build(),
				this.languages.build(),
				this.supported.build(),
				this.accepts.build(),
				this.acceptCharsets.build(),
				this.acceptLanguages.build(),
				this.errorVariant).
				execute();
	}

	public static ContentNegotiator newInstance() {
		return new ContentNegotiator();
	}

}


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

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;

import org.ldp4j.application.ext.Namespaces;

import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps;

/**
 * An immutable {@code Namespaces} implementation. <br>
 *
 * An instance of {@code ImmutableNamespaces} contains its own data and will never
 * change. ImmutableNamespaces is convenient for public static final namespaces
 * ("constant namespaces") and implements the copy-on-write pattern to easily
 * make a "defensive copy" of a namespaces provided to your class by a caller.
 */
public final class ImmutableNamespaces implements Namespaces {

	private static final String LINE_SEPARATOR = System.lineSeparator();

	private final Map<String,String> map;

	private ImmutableNamespaces(Map<String,String> map) {
		this.map=Maps.newHashMap(map);
	}

	/**
	 * Create a new empty instance
	 */
	public ImmutableNamespaces() {
		this(Maps.<String,String>newLinkedHashMap());
	}

	/**
	 * Create a copy of the namespaces which includes a mapping between a given
	 * prefix and namespace URI.
	 *
	 * @param prefix
	 *            the prefix to be added
	 * @param namespaceURI
	 *            the namespace URI to be mapped to the prefix
	 * @return a copy of the instance that includes the mapping between the
	 *         specified namespace URI and prefix.
	 * @throws NullPointerException if the prefix or namespace URI are null.
	 */
	public ImmutableNamespaces withPrefix(String prefix, String namespaceURI) {
		Objects.requireNonNull(prefix, "Prefix cannot be null");
		Objects.requireNonNull(namespaceURI, "Namespace URI cannot be null");
		ImmutableNamespaces result=new ImmutableNamespaces(this.map);
		result.map.put(prefix, namespaceURI);
		return result;
	}

	/**
	 * Create a copy of the namespaces without the prefixes.
	 *
	 * @param prefixes
	 *            the prefixes to be excluded from the copy
	 * @return a copy of the instance that excludes the mappings for the
	 *         specified prefixes.
	 */
	public ImmutableNamespaces withoutPrefix(String... prefixes) {
		ImmutableNamespaces result=new ImmutableNamespaces(this.map);
		for(String prefix:prefixes) {
			result.map.remove(prefix);
		}
		return result;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public Set<String> getDeclaredPrefixes() {
		return ImmutableSet.copyOf(this.map.keySet());
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public String getNamespaceURI(String prefix) {
		return this.map.get(prefix);
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public String getPrefix(String namespaceURI) {
		for(Entry<String,String> entry:this.map.entrySet()) {
			if(entry.getValue().equals(namespaceURI)) {
				return entry.getKey();
			}
		}
		return null;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public List<String> getPrefixes(String namespaceURI) {
		List<String> list=new ArrayList<String>();
		for(Entry<String,String> entry:this.map.entrySet()) {
			if(entry.getValue().equals(namespaceURI)) {
				list.add(entry.getKey());
			}
		}
		return list;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public String toString() {
		StringBuilder builder=new StringBuilder();
		builder.append("Namespaces {");
		for(Entry<String,String> entry:this.map.entrySet()) {
			String namespaceURI = entry.getValue();
			String prefix = entry.getKey();
			String line=String.format("  - \"%s\" : \"%s\" [%s]",prefix,namespaceURI,namespaceURI.getClass().getName());
			builder.append(LINE_SEPARATOR).append(line);
		}
		if(!this.map.isEmpty()) {
			builder.append(LINE_SEPARATOR);
		}
		builder.append("}");
		return builder.toString();
	}

}


/**
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   This file is part of the LDP4j Project:
 *     http://www.ldp4j.org/
 *
 *   Center for Open Middleware
 *     http://www.centeropenmiddleware.com/
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   Copyright (C) 2014-2016 Center for Open Middleware.
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *             http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   Artifact    : org.ldp4j.commons:ldp4j-commons-core:0.2.2
 *   Bundle      : ldp4j-commons-core-0.2.2.jar
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 */
package org.ldp4j.xml;

/**
 * This class contains various methods for checking QNames, NCNames etc. The
 * implementation is based on the W3C
 * namespaces in XML specification.
 *
 */
public final class XMLUtils {

	/** &lt; shortcut */
	private static final String LT = "<";

	/** &gt; shortcut */
	private static final String GT = ">";

	/** &quot; shortcut */
	private static final String QUOT = """;

	/** &amp; shortcut */
	private static final String AMP = "&";

	/** &apos; shortcut */
	private static final String APOS = "'";

	private XMLUtils() {
	}

	/**
	 * Determines if a character sequence is a Name. An Name is a string which
	 * starts with a Name start character and is followed by zero or more Name
	 * characters.
	 *
	 * @param s
	 *            The character sequence to be tested.
	 * @return {@code true} if {@code s} is a Name, otherwise {@code false}.
	 */
	public static boolean isName(final CharSequence s) {
		return Validators.name().isValid(s);
	}

	/**
	 * Determines if a character sequence is an NCName (Non-Colonised Name). An
	 * NCName is a string which starts with an NCName start character and is
	 * followed by zero or more NCName characters.
	 *
	 * @param s
	 *            The character sequence to be tested.
	 * @return {@code true} if {@code s} is an NCName, otherwise {@code false}.
	 */
	public static boolean isNCName(final CharSequence s) {
		return Validators.ncName().isValid(s);
	}

	/**
	 * Determines if a character sequence is a QName. A QName is either an
	 * NCName (LocalName), or an NCName followed by a colon followed by another
	 * NCName (where the first NCName is referred to as the 'Prefix Name' and
	 * the second NCName is referred to as the 'Local Name' - i.e.
	 * PrefixName:LocalName).
	 *
	 * @param s
	 *            The character sequence to be tested.
	 * @return {@code true} if {@code s} is a QName, otherwise {@code false}.
	 */
	public static boolean isQName(final CharSequence s) {
		return Validators.qName().isValid(s);
	}

	/**
	 * Escapes a character sequence so that it is valid XML.
	 *
	 * @param s
	 *            The character sequence.
	 * @return The escaped version of the character sequence.
	 */
	public static String escapeXML(final CharSequence s) {
		final StringBuilder sb = new StringBuilder(s.length() * 2);
		final CodePointIterator iterator = new CodePointIterator(s);
		while (iterator.hasNext()) {
			final int codePoint = iterator.next();
			if (codePoint == '<') {
				sb.append(LT);
			} else if (codePoint == '>') {
				sb.append(GT);
			} else if (codePoint == '\"') {
				sb.append(QUOT);
			} else if (codePoint == '&') {
				sb.append(AMP);
			} else if (codePoint == '\'') {
				sb.append(APOS);
			} else {
				sb.appendCodePoint(codePoint);
			}
		}
		return sb.toString();
	}

}


/**
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   This file is part of the LDP4j Project:
 *     http://www.ldp4j.org/
 *
 *   Center for Open Middleware
 *     http://www.centeropenmiddleware.com/
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   Copyright (C) 2014-2016 Center for Open Middleware.
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *             http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   Artifact    : org.ldp4j.framework:ldp4j-application-kernel-jpa:0.2.2
 *   Bundle      : ldp4j-application-kernel-jpa-0.2.2.jar
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 */
package org.ldp4j.application.kernel.persistence.encoding;

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

import org.ldp4j.application.data.Name;
import org.ldp4j.application.data.NamingScheme;

/**
 * Identifier value encoder.
 *
 * @author Miguel Esteban Gutiérrez
 */
final class ValueEncoder extends AbstractEncoder {

	/**
	 * {@inheritDoc}
	 */
	@Override
	protected Serializable prepare(Name<?> name) {
		return name.id();
	}

	/**
	 * {@inheritDoc}
	 */
	@SuppressWarnings("unchecked")
	@Override
	protected <T extends Serializable> Name<T> assemble(Serializable subject) throws IOException {
		return (Name<T>)NamingScheme.getDefault().name(subject);
	}

}


/**
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   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.ext.InvalidQueryException;
import org.ldp4j.application.ext.Query;
import org.ldp4j.application.ext.Queryable;
import org.ldp4j.application.ext.annotations.Resource;
import org.ldp4j.application.session.ReadSession;
import org.ldp4j.application.session.ResourceSnapshot;

/**
 * An example resource handler that can be queried.
 */
@Resource(
	id=QueryableResourceHandler.ID
)
public class QueryableResourceHandler extends InMemoryResourceHandler implements Queryable {

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

	/**
	 * The query parameter name to provide to raise a failure in the processing
	 * of the query.
	 */
	public static final String FAILURE = "failure";

	/**
	 * Create a new instance.
	 */
	public QueryableResourceHandler() {
		super("QueryableResource");
	}

	/**
	 * Instead of retrieving the contents of the resource, return a description
	 * of the query received.
	 */
	@Override
	public DataSet query(ResourceSnapshot resource, Query query, ReadSession session) throws InvalidQueryException {
		return QuerySupport.getDescription(resource.name(), query);
	}

}


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

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

import com.google.common.collect.Maps;

/**
 * An example in-memory container handler.
 */
public abstract class InMemoryContainerHandler extends InMemoryResourceHandler implements ContainerHandler {

	private final Map<Name<String>,NameProvider> nameProviders;

	protected InMemoryContainerHandler(String handlerName) {
		super(handlerName);
		this.nameProviders=Maps.newLinkedHashMap();
	}

	/**
	 * Add a name provider for a container resource.
	 *
	 * @param containerName
	 *            the name of the container.
	 * @param provider
	 *            the name provider.
	 */
	public final void addNameProvider(Name<String> containerName, NameProvider provider) {
		this.nameProviders.put(containerName, provider);
	}

	/**
	 * Return the name provider registered for a given container resource.
	 *
	 * @param containerName
	 *            the name of the container.
	 * @return the provider for the specified container resource.
	 */
	public final NameProvider nameProvider(Name<?> containerName) {
		NameProvider result = this.nameProviders.get(containerName);
		if(result==null) {
			throw new ApplicationRuntimeException("Unknown container '"+containerName+"'");
		}
		return result;
	}

}


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

final class MoreStrings {

	private MoreStrings() {
	}

	/**
	 * Check that the given {@code CharSequence} is neither {@code null} nor of
	 * length 0.
	 *

	 * Note: this method returns {@code true} for a {@code CharSequence} that
	 * purely consists of whitespace.
	 *

	 *
	 *

	 * MoreStrings.hasLength(null) = false
	 * MoreStrings.hasLength("") = false
	 * MoreStrings.hasLength(" ") = true
	 * MoreStrings.hasLength("Hello") = true
	 *

	 *
	 * @param cs
	 *            the {@code CharSequence} to check (may be {@code null})
	 * @return {@code true} if the {@code CharSequence} is not {@code null} and
	 *         has length; {@code false} otherwise
	 */
	static boolean hasLength(final CharSequence cs) {
		return cs != null && cs.length() > 0;
	}

	/**
	 * Check that the given {@code String} is neither {@code null} nor of length
	 * 0.
	 *

	 * Note: this method returns {@code true} for a {@code String} that purely
	 * consists of whitespace.
	 *
	 * @param str
	 *            the {@code String} to check (may be {@code null})
	 * @return {@code true} if the {@code String} is not {@code null} and has
	 *         length
	 * @see #hasLength(CharSequence)
	 */
	static boolean hasLength(final String str) {
		return hasLength((CharSequence) str);
	}

	/**
	 *

	 * Compares two CharSequences, returning {@code true} if they represent
	 * equal sequences of characters, ignoring case.
	 *

	 *
	 *

	 * {@code null}s are handled without exceptions. Two {@code null}
	 * references are considered equal. Comparison is case insensitive.
	 *

	 *
	 *

	 * equalsIgnoreCase(null, null)   = true
	 * equalsIgnoreCase(null, "abc")  = false
	 * equalsIgnoreCase("abc", null)  = false
	 * equalsIgnoreCase("abc", "abc") = true
	 * equalsIgnoreCase("abc", "ABC") = true
	 *

	 *
	 * @param str1
	 *            the first CharSequence, may be {@code null}
	 * @param str2
	 *            the second CharSequence, may be {@code null}
	 * @return {@code true} if the CharSequence are equal, case insensitive,
	 *         or both {@code null}; {@code false} otherwise
	 */
	static boolean equalsIgnoreCase(final CharSequence str1, final CharSequence str2) {
		if(str1==str2) {
			return true;
		} else if(str1==null||str2==null) {
			return false;
		} else if(str1.length()!=str2.length()) {
			return false;
		} else {
			return regionMatches(str1,true,0,str2,0,str1.length());
		}
	}

	/**
	 * Tests if two {@link CharSequence charsequence} regions are equal.
	 *

	 * A region of the {@code target} charsequence is compared to a region of
	 * the {@code other} charsequence. The result is {@code true} if these regions
	 * represent character sequences that are the same, ignoring case if and
	 * only if {@code ignoreCase} is true. The region of the {@code target}
	 * charsequence to be compared begins at index {@code offset}
	 * , whereas the region of the {@code other} charsequence begins at index {@code start}. Both
	 * regions have a length {@code length}.
	 *

	 * The result is {@code false} if and only if at least one of the following
	 * is true:
	 *

	 *
{@code toffset} is negative.
	 *
{@code ooffset} is negative.
	 *
{@code toffset+length} is greater than the length of {@code target}
	 * argument.
	 *
{@code ooffset+length} is greater than the length of {@code other}
	 * argument.
	 *
{@code ignoreCase} is {@code false} and there is some nonnegative
	 * integer k less than {@code length} such that:

	 *
	 *

	 * target.charAt(toffset + k) != other.charAt(ooffset + k)
	 *

	 *
	 *

	 *
{@code ignoreCase} is {@code true} and there is some nonnegative
	 * integer k less than {@code length} such that:

	 *
	 *

	 * let:
	 *   char u1=Character.toUpperCase(target.charAt(toffset + k))
	 *   char u2=Character.toUpperCase(other.charAt(ooffset + k))
	 *
	 * u1! = u2 && Character.toLowerCase(u1) != Character.toLowerCase(u2)
	 *

	 *

	 *
	 * @param target
	 *            the first charsequence to compare.
	 * @param ignoreCase
	 *            if {@code true}, ignore case when comparing characters.
	 * @param toffset
	 *            the starting offset of the subregion of the {@code target}
	 *            argument.
	 * @param other
	 *            the second charsequence to compare.
	 * @param ooffset
	 *            the starting offset of the subregion in the {@code other}
	 *            argument.
	 * @param length
	 *            the number of characters to compare.
	 * @return {@code true} if the specified subregion of the {@code target}
	 *         argument matches the specified subregion of the {@code other}
	 *         argument; {@code false} otherwise. Whether the matching is exact
	 *         or case insensitive depends on the {@code ignoreCase} argument.
	 */
	static boolean regionMatches(
			final CharSequence target,
			final boolean ignoreCase,
			final int toffset,
			final CharSequence other,
			final int ooffset,
			final int length) {
		boolean result;
		if (target instanceof String && other instanceof String) {
			result=delegateCheck((String)target,ignoreCase,toffset,ooffset,length,(String) other);
		} else {
			result=carryOutCheck(target, ignoreCase, toffset, other, ooffset, length);
		}
		return result;
	}

	private static boolean delegateCheck(
			final String target,
			final boolean ignoreCase,
			final int offset,
			final int start,
			final int length,
			final String other) {
		return target.regionMatches(ignoreCase,offset,other,start,length);
	}

	private static boolean carryOutCheck(
			final CharSequence target,
			final boolean ignoreCase,
			final int offset,
			final CharSequence other,
			final int start,
			final int length) {
		int index1=offset;
		int index2=start;
		int tmpLen=length;
		if(outOfBounds(target,offset,length) || outOfBounds(other,start,length)) {
			return false;
		}
		while(tmpLen-->0) { // NOSONAR
			final char c1=target.charAt(index1++);
			final char c2=other.charAt(index2++);
			if(c1==c2) {
				continue;
			}
			/**
			 * If characters don't match but case may be ignored...
			 */
			if(ignoreCase) {
				/**
				 * ... try converting both characters to uppercase. If the
				 * results match, then the comparison scan should continue.
				 */
				final char u1=Character.toUpperCase(c1);
				final char u2=Character.toUpperCase(c2);
				if(u1==u2) {
					continue;
				}
				/**
				 * Unfortunately, conversion to uppercase does not work properly
				 * in certain alphabets (e.g., Georgian) that have strange rules
				 * about case conversion, and does not take into consideration
				 * alternative variants in certain UNICODE blocks (e.g., Greek
				 * and Coptic, Latin Extended-A). So we need to make one last
				 * check before exiting.
				 */
				if(Character.toLowerCase(u1)==Character.toLowerCase(u2)) {
					continue;
				}
			}
			return false;
		}
		return true;
	}

	private static boolean outOfBounds(final CharSequence str, final int offset, final int length) {
		return offset < 0 || offset > (long) str.length() - length;
	}

}