/**
* Copyright (C) 2012-2014 Gist Labs, LLC. (http://gistlabs.com)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package com.gistlabs.mechanize.requestor;
import java.io.File;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.entity.mime.MultipartEntity;
import org.apache.http.entity.mime.content.ContentBody;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.entity.mime.content.StringBody;
import org.apache.http.message.BasicNameValuePair;
import com.gistlabs.mechanize.exceptions.MechanizeExceptionFactory;
import com.gistlabs.mechanize.headers.Header;
import com.gistlabs.mechanize.headers.Headers;
import com.gistlabs.mechanize.parameters.Parameter;
import com.gistlabs.mechanize.parameters.Parameters;
import com.gistlabs.mechanize.util.apache.URIBuilder;
import com.gistlabs.mechanize.util.apache.URLEncodedUtils;
public class RequestBuilder<Resource> {
private final PageRequestor<Resource> requestor;
private String uri;
private final Parameters parameters = new Parameters();
private final Headers setHeaders = new Headers();
private final Headers addHeaders = new Headers();
private final Map<String, ContentBody> files = new HashMap<String, ContentBody>();
private boolean isMultiPart = false;
public RequestBuilder(final PageRequestor<Resource> requestor) {
this.requestor = requestor;
}
public RequestBuilder(final PageRequestor<Resource> requestor, final String uri) {
this(requestor);
setUri(uri);
}
private void setUri(final String uri) {
this.uri = uri;
if(uri.contains("?"))
for(NameValuePair param : URLEncodedUtils.parse(uri.substring(uri.indexOf('?') + 1), Charset.forName("UTF-8")))
parameters.add(param.getName(), param.getValue());
}
public RequestBuilder<Resource> multiPart() {
this.isMultiPart = true;
return this;
}
public RequestBuilder<Resource> add(final String name, final String ... values) {
parameters.add(name, values);
return this;
}
public RequestBuilder<Resource> set(final String name, final String ... values) {
parameters.set(name, values);
return this;
}
public RequestBuilder<Resource> set(final Parameters parameters) {
for(String name : parameters.getNames())
set(name, parameters.get(name));
return this;
}
public RequestBuilder<Resource> add(final Parameters parameters) {
for(String name : parameters.getNames())
add(name, parameters.get(name));
return this;
}
public RequestBuilder<Resource> addHeader(final String name, final String ... values) {
addHeaders.add(name, values);
return this;
}
public RequestBuilder<Resource> setHeader(final String name, final String ... values) {
setHeaders.set(name, values);
return this;
}
public RequestBuilder<Resource> setHeaders(final Headers headers) {
for(String name : headers.getNames())
setHeader(name, headers.get(name));
return this;
}
public RequestBuilder<Resource> addHeaders(final Headers headers) {
for(String name :headers.getNames())
addHeader(name, headers.get(name));
return this;
}
public RequestBuilder<Resource> accept(final String contentType) {
this.setHeader("Accept", contentType);
return this;
}
/** Adds a file to the request also making the request to become a multi-part post request or removes any file registered
* under the given name if the file value is null. */
public RequestBuilder<Resource> set(final String name, final File file) {
return set(name, file != null ? new FileBody(file) : null);
}
/** Adds an ContentBody object. */
public RequestBuilder<Resource> set(final String name, final ContentBody contentBody) {
if(contentBody != null)
files.put(name, contentBody);
else
files.remove(name);
return this;
}
public Parameters parameters() {
return parameters;
}
public <T extends Resource> T get() {
if(hasFiles())
throw new UnsupportedOperationException("Files can not be send using a get request");
HttpRequestBase request = composeGetRequest(uri, parameters);
buildHeaders(request);
return requestor.request(request);
}
public <T extends Resource> T post() {
// HttpPost request = (!hasFiles()) || isMultiPart ? composePostRequest(getBaseUri(), parameters) :
// composeMultiPartFormRequest(getBaseUri(), parameters, files);
HttpPost request = (hasFiles() || isMultiPart) ? composeMultiPartFormRequest(getBaseUri(), parameters, files) :
composePostRequest(getBaseUri(), parameters);
buildHeaders(request);
return requestor.request(request);
}
private boolean hasFiles() {
return !files.isEmpty();
}
private String getBaseUri() {
return uri.contains("?") ? uri.substring(0, uri.indexOf('?')) : uri;
}
private void buildHeaders(final HttpRequestBase request) {
for(Header header : this.setHeaders)
for(String value : header)
request.setHeader(header.getName(), value);
for(Header header : this.addHeaders)
for(String value : header)
request.addHeader(header.getName(), value);
}
private HttpRequestBase composeGetRequest(String uri, final Parameters parameters) {
try {
URIBuilder builder = new URIBuilder(uri);
for(Parameter param : parameters)
if(param.isSingleValue())
builder.setParameter(param.getName(), param.getValue());
else
for(String value : param.getValues())
builder.addParameter(param.getName(), value);
URI requestURI = builder.build();
uri = requestURI.toString();
return new HttpGet(requestURI);
}
catch(URISyntaxException e) {
throw MechanizeExceptionFactory.newException(e);
}
}
private HttpPost composePostRequest(final String uri, final Parameters parameters) {
HttpPost request = new HttpPost(uri);
List<NameValuePair> formparams = new ArrayList<NameValuePair>();
for(Parameter param : parameters)
if(param.isSingleValue())
formparams.add(new BasicNameValuePair(param.getName(), param.getValue()));
else
for(String value : param.getValues())
formparams.add(new BasicNameValuePair(param.getName(), value));
try {
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(formparams, "UTF-8");
request.setEntity(entity);
} catch (UnsupportedEncodingException e) {
throw MechanizeExceptionFactory.newException(e);
}
return request;
}
private HttpPost composeMultiPartFormRequest(final String uri, final Parameters parameters, final Map<String, ContentBody> files) {
HttpPost request = new HttpPost(uri);
MultipartEntity multiPartEntity = new MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE);
try {
Charset utf8 = Charset.forName("UTF-8");
for(Parameter param : parameters)
if(param.isSingleValue())
multiPartEntity.addPart(param.getName(), new StringBody(param.getValue(), utf8));
else
for(String value : param.getValues())
multiPartEntity.addPart(param.getName(), new StringBody(value, utf8));
} catch (UnsupportedEncodingException e) {
throw MechanizeExceptionFactory.newException(e);
}
List<String> fileNames = new ArrayList<String>(files.keySet());
Collections.sort(fileNames);
for(String name : fileNames) {
ContentBody contentBody = files.get(name);
multiPartEntity.addPart(name, contentBody);
}
request.setEntity(multiPartEntity);
return request;
}
}