/**
* Copyright 2005-2014 Restlet
*
* The contents of this file are subject to the terms of one of the following
* open source licenses: Apache 2.0 or or EPL 1.0 (the "Licenses"). You can
* select the license that you prefer but you may not use this file except in
* compliance with one of these Licenses.
*
* You can obtain a copy of the Apache 2.0 license at
* http://www.opensource.org/licenses/apache-2.0
*
* You can obtain a copy of the EPL 1.0 license at
* http://www.opensource.org/licenses/eclipse-1.0
*
* See the Licenses for the specific language governing permissions and
* limitations under the Licenses.
*
* Alternatively, you can obtain a royalty free commercial license with less
* limitations, transferable or non-transferable, directly at
* http://restlet.com/products/restlet-framework
*
* Restlet is a registered trademark of Restlet S.A.S.
*/
package org.restlet.ext.jaxrs.internal.core;
import java.net.URI;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import javax.ws.rs.core.CacheControl;
import javax.ws.rs.core.Cookie;
import javax.ws.rs.core.EntityTag;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.NewCookie;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.ResponseBuilder;
import javax.ws.rs.core.Variant;
import org.restlet.data.Dimension;
import org.restlet.ext.jaxrs.internal.util.Converter;
import org.restlet.ext.jaxrs.internal.util.Util;
/**
* Implementation of the {@link ResponseBuilder}.
*
* @author Stephan Koops
*/
public class ResponseBuilderImpl extends ResponseBuilder {
private MultivaluedMap<String, Object> metadata;
private Map<String, NewCookie> newCookies;
private ResponseImpl response;
/**
* Creates a new Response Builder
*/
public ResponseBuilderImpl() {
}
/**
* Create a Response instance from the current ResponseBuilder. The builder
* is reset to a blank state equivalent to calling the ok method.
*
* @return a Response instance
* @see javax.ws.rs.core.Response.ResponseBuilder#build()
*/
@Override
public Response build() {
if (this.response == null) {
return new ResponseImpl();
}
final Response r = this.response;
if (this.newCookies != null) {
final MultivaluedMap<String, Object> metadata = getMetadata();
for (final NewCookie cookie : this.newCookies.values()) {
metadata.add(HttpHeaders.SET_COOKIE, cookie);
}
this.newCookies = null;
}
this.response = null;
this.metadata = null;
return r;
}
/**
* Set the cache control data on the ResponseBuilder.
*
* @param cacheControl
* the cache control directives
* @return the updated ResponseBuilder
* @see javax.ws.rs.core.Response.ResponseBuilder#cacheControl(javax.ws.rs.core.CacheControl)
*/
@Override
public ResponseBuilder cacheControl(CacheControl cacheControl) {
if (cacheControl == null) {
getMetadata().remove(HttpHeaders.CACHE_CONTROL);
} else {
getMetadata().putSingle(HttpHeaders.CACHE_CONTROL, cacheControl);
}
return this;
}
/**
* @see javax.ws.rs.core.Response.ResponseBuilder#clone()
*/
@Override
public ResponseBuilderImpl clone() {
final ResponseBuilderImpl newRb = new ResponseBuilderImpl();
newRb.response = this.response.clone();
newRb.newCookies = new HashMap<String, NewCookie>(this.newCookies);
// metadatas are read from the response.
return newRb;
}
/**
* Set the content location on the ResponseBuilder.
*
*
* @param location
* the content location
* @return the updated ResponseBuilder
* @see javax.ws.rs.core.Response.ResponseBuilder#contentLocation(java.net.URI)
*/
@Override
public ResponseBuilder contentLocation(URI location) {
if (location == null) {
getMetadata().remove(HttpHeaders.CONTENT_LOCATION);
} else {
getMetadata().putSingle(HttpHeaders.CONTENT_LOCATION,
location.toASCIIString());
}
return this;
}
/**
* Add cookies to the ResponseBuilder. If more than one cookie with the same
* is supplied, later ones overwrite earlier ones.
*
* @param cookies
* new cookies that will accompany the response.
* @return the updated ResponseBuilder
* @see javax.ws.rs.core.Response.ResponseBuilder#cookie(javax.ws.rs.core.NewCookie)
*/
@Override
public ResponseBuilder cookie(NewCookie... cookies) {
final Map<String, NewCookie> newCookies = getNewCookies();
for (final NewCookie cookie : cookies) {
if (cookie != null) {
newCookies.put(cookie.getName(), cookie);
}
}
return this;
}
/**
* Set the language on the ResponseBuilder. <br>
* This method is not required by the JAX-RS API bu is used from
* {@link #variant(Variant)}.
*
* @param encoding
* the encoding of the response entity
* @return the updated ResponseBuilder
*/
public ResponseBuilder encoding(String encoding) {
if (encoding == null) {
getMetadata().remove(HttpHeaders.CONTENT_ENCODING);
} else {
getMetadata().putSingle(HttpHeaders.CONTENT_ENCODING, encoding);
}
return this;
}
/**
* Set the entity on the ResponseBuilder.
*
*
* @param entity
* the response entity
* @return the updated ResponseBuilder
* @see javax.ws.rs.core.Response.ResponseBuilder#entity(java.lang.Object)
*/
@Override
public ResponseBuilder entity(Object entity) {
getResponse().setEntity(entity);
return this;
}
/**
* @see javax.ws.rs.core.Response.ResponseBuilder#expires(java.util.Date)
*/
@Override
public ResponseBuilder expires(Date expires) {
if (expires == null) {
getMetadata().remove(HttpHeaders.EXPIRES);
} else {
getMetadata().putSingle(HttpHeaders.EXPIRES, expires);
}
return this;
}
MultivaluedMap<String, Object> getMetadata() {
if (this.metadata == null) {
this.metadata = getResponse().getMetadata();
}
return this.metadata;
}
Map<String, NewCookie> getNewCookies() {
if (this.newCookies == null) {
this.newCookies = new HashMap<String, NewCookie>();
}
return this.newCookies;
}
ResponseImpl getResponse() {
if (this.response == null) {
this.response = new ResponseImpl();
}
return this.response;
}
/**
* Add a header to the ResponseBuilder.
*
* @param name
* the name of the header
* @param value
* the value of the header, the header will be serialized using
* its toString method. If null then all current headers of the
* same name will be removed.
* @return the updated ResponseBuilder
* @see javax.ws.rs.core.Response.ResponseBuilder#header(String, Object)
*/
@Override
public ResponseBuilder header(String name, Object value) {
if (name == null) {
throw new IllegalArgumentException(
"You must give a name of the header");
}
if (name.equals(HttpHeaders.SET_COOKIE)) {
if (value == null) {
this.newCookies.clear();
} else if (value instanceof NewCookie) {
cookie((NewCookie) value);
} else if (value instanceof Cookie) {
cookie(new NewCookie((Cookie) value));
} else {
cookie(NewCookie.valueOf(value.toString()));
}
} else {
if (value == null) {
getMetadata().remove(name);
} else {
getMetadata().add(name, value);
}
}
return this;
}
/**
* Set the language on the ResponseBuilder.
*
*
* @param language
* the language of the response entity
* @return the updated ResponseBuilder
* @see javax.ws.rs.core.Response.ResponseBuilder#language(java.lang.String)
*/
@Override
public ResponseBuilder language(Locale language) {
if (language == null) {
getMetadata().remove(HttpHeaders.CONTENT_LANGUAGE);
} else {
getMetadata().putSingle(HttpHeaders.CONTENT_LANGUAGE, language);
}
return this;
}
/**
* Set the language on the ResponseBuilder.
*
*
* @param language
* the language of the response entity
* @return the updated ResponseBuilder
* @see javax.ws.rs.core.Response.ResponseBuilder#language(java.lang.String)
*/
@Override
public ResponseBuilder language(String language) {
if (language == null) {
getMetadata().remove(HttpHeaders.CONTENT_LANGUAGE);
} else {
getMetadata().putSingle(HttpHeaders.CONTENT_LANGUAGE, language);
}
return this;
}
/**
* Set the last modified date on the ResponseBuilder.
*
*
* @param lastModified
* the last modified date
* @return the updated ResponseBuilder
* @see javax.ws.rs.core.Response.ResponseBuilder#lastModified(java.util.Date)
*/
@Override
public ResponseBuilder lastModified(Date lastModified) {
if (lastModified == null) {
getMetadata().remove(HttpHeaders.LAST_MODIFIED);
} else {
getMetadata().putSingle(HttpHeaders.LAST_MODIFIED, lastModified);
}
return this;
}
/**
* Set the location on the ResponseBuilder.
*
*
* @param location
* the location
* @return the updated ResponseBuilder
* @see javax.ws.rs.core.Response.ResponseBuilder#location(java.net.URI)
*/
@Override
public ResponseBuilder location(URI location) {
if (location == null) {
getMetadata().remove(HttpHeaders.LOCATION);
} else {
getMetadata().putSingle(HttpHeaders.LOCATION, location);
}
return this;
}
/**
* Set the status on the ResponseBuilder.
*
* @param status
* the response status
* @return the updated ResponseBuilder
* @throws IllegalArgumentException
* if status is less than 100 or greater than 599.
* @see javax.ws.rs.core.Response.ResponseBuilder#status(int)
*/
@Override
public ResponseBuilder status(int status) {
if (status < 100 || status >= 600)
throw new IllegalArgumentException(
"The status must be between 100 (inclusive) and 600 (exclusive), but is "
+ status);
if (this.response == null) {
this.response = new ResponseImpl(status);
} else {
this.response.setStatus(status);
}
return this;
}
/**
* Set the entity tag on the ResponseBuilder.
*
* @param tag
* the entity tag
* @return the updated ResponseBuilder
* @see javax.ws.rs.core.Response.ResponseBuilder#tag(javax.ws.rs.core.EntityTag)
*/
@Override
public ResponseBuilder tag(EntityTag tag) {
if (tag == null) {
getMetadata().remove(HttpHeaders.ETAG);
} else {
getMetadata().putSingle(HttpHeaders.ETAG, tag);
}
return this;
}
/**
* Set a strong entity tag on the ResponseBuilder. This is a shortcut for
* <code>tag(new EntityTag(<i>value</i>))</code>.
*
* @param tag
* the string content of a strong entity tag. The JAX-RS runtime
* will quote the supplied value when creating the header.
* @return the updated ResponseBuilder
* @see javax.ws.rs.core.Response.ResponseBuilder#tag(java.lang.String)
*/
@Override
public ResponseBuilder tag(String tag) {
if (tag == null) {
tag((EntityTag) null);
} else {
tag(new EntityTag(tag, false));
}
return this;
}
/**
* Set the response media type on the ResponseBuilder.
*
* @see javax.ws.rs.core.Response.ResponseBuilder#type(javax.ws.rs.core.MediaType)
*/
@Override
public ResponseBuilder type(MediaType type) {
if (type == null) {
getMetadata().remove(HttpHeaders.CONTENT_TYPE);
} else {
getMetadata().putSingle(HttpHeaders.CONTENT_TYPE, type);
}
return this;
}
/**
* Set the response media type on the ResponseBuilder.
*
* @param type
* the media type of the response entity
* @return the updated ResponseBuilder
* @throws IllegalArgumentException
* if type cannot be parsed
* @see javax.ws.rs.core.Response.ResponseBuilder#type(java.lang.String)
*/
@Override
public ResponseBuilder type(String type) {
if (type == null) {
return type((MediaType) null);
}
return type(MediaType.valueOf(type));
}
/**
* Set representation metadata on the ResponseBuilder.
*
* @param variant
* metadata of the response entity
* @return the updated ResponseBuilder
* @see javax.ws.rs.core.Response.ResponseBuilder#variant(javax.ws.rs.core.Variant)
*/
@Override
public ResponseBuilder variant(Variant variant) {
if (variant == null) {
this.language((String) null);
this.encoding(null);
this.type((MediaType) null);
} else {
this.language(variant.getLanguage());
this.encoding(variant.getEncoding());
this.type(variant.getMediaType());
}
return this;
}
/**
* Add a Vary header that lists the available variants.
*
* @param variants
* a list of available representation variants, a null value will
* remove an existing value for vary.
* @return the updated ResponseBuilder
* @see javax.ws.rs.core.Response.ResponseBuilder#variants(java.util.List)
*/
@Override
public ResponseBuilder variants(List<Variant> variants) {
if (variants == null) {
getMetadata().remove(HttpHeaders.VARY);
return this;
}
// NICE add entity header with further information
// give links, use extension mapping. Was macht Restlet da schon?
final Set<String> encodings = new HashSet<String>();
final Set<Locale> languages = new HashSet<Locale>();
final Set<MediaType> mediaTypes = new HashSet<MediaType>();
final Set<String> charsets = new HashSet<String>();
for (final Variant variant : variants) {
final String encoding = variant.getEncoding();
if (encoding != null) {
encodings.add(encoding);
}
final Locale language = variant.getLanguage();
if (language != null) {
languages.add(language);
}
final MediaType mediaType = variant.getMediaType();
if (mediaType != null) {
mediaTypes.add(Converter.getMediaTypeWithoutParams(mediaType));
}
final String charset = Converter.getCharset(mediaType);
if (charset != null) {
charsets.add(charset);
}
}
Set<Dimension> dimensions;
dimensions = org.restlet.Response.getCurrent().getDimensions();
if (encodings.size() > 1) {
dimensions.add(Dimension.ENCODING);
}
if (languages.size() > 1) {
dimensions.add(Dimension.LANGUAGE);
}
if (mediaTypes.size() > 1) {
dimensions.add(Dimension.MEDIA_TYPE);
}
if (charsets.size() > 1) {
dimensions.add(Dimension.CHARACTER_SET);
}
final String vary = Util.formatDimensions(dimensions);
if (vary != null) {
getMetadata().putSingle(HttpHeaders.VARY, vary);
}
return this;
}
}