001/**
002 * Copyright 2015 DuraSpace, Inc.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.fcrepo.client;
018
019import static org.fcrepo.client.FedoraHeaderConstants.ACCEPT;
020import static org.fcrepo.client.FedoraHeaderConstants.IF_MODIFIED_SINCE;
021import static org.fcrepo.client.FedoraHeaderConstants.IF_NONE_MATCH;
022import static org.fcrepo.client.FedoraHeaderConstants.PREFER;
023import static org.fcrepo.client.FedoraHeaderConstants.RANGE;
024
025import java.net.URI;
026import java.util.List;
027import java.util.StringJoiner;
028import java.util.stream.Collectors;
029
030import org.apache.http.client.config.RequestConfig;
031import org.apache.http.client.methods.HttpRequestBase;
032
033/**
034 * Builds a GET request to retrieve the content of a resource from the Fedora HTTP API
035 * 
036 * @author bbpennel
037 */
038public class GetBuilder extends
039        RequestBuilder {
040
041    /**
042     * Construct a GetBuilder
043     * 
044     * @param uri the target
045     * @param client the client for this request
046     */
047    public GetBuilder(final URI uri, final FcrepoClient client) {
048        super(uri, client);
049    }
050
051    @Override
052    protected HttpRequestBase createRequest() {
053        return HttpMethods.GET.createRequest(targetUri);
054    }
055
056    /**
057     * Add the accept header to this request to negotiate the response format.
058     * 
059     * @param mediaType media type to set as the accept header. It should be a value from one of the allowed RDF
060     *        source formats supported by Fedora.
061     * @return this builder
062     */
063    public GetBuilder accept(final String mediaType) {
064        if (mediaType != null) {
065            request.setHeader(ACCEPT, mediaType);
066        }
067        return this;
068    }
069
070    /**
071     * Set the byte range of content to retrieve
072     * 
073     * @param rangeStart beginning byte index
074     * @param rangeEnd ending byte index
075     * @return this builder
076     */
077    public GetBuilder range(final Long rangeStart, final Long rangeEnd) {
078        if (rangeStart != null || rangeEnd != null) {
079            String range = "bytes=";
080            if (rangeStart != null && rangeStart.longValue() > -1L) {
081                range += rangeStart.toString();
082            }
083            range += "-";
084            if (rangeEnd != null && rangeEnd.longValue() > -1L) {
085                range += rangeEnd.toString();
086            }
087            request.setHeader(RANGE, range);
088        }
089        return this;
090    }
091
092    /**
093     * Set the prefer header for this request to minimal, to indicate that only triples directly related to a resource
094     * should be returned.
095     * 
096     * @return this builder
097     */
098    public GetBuilder preferMinimal() {
099        request.setHeader(PREFER, buildPrefer("minimal", null, null));
100        return this;
101    }
102
103    /**
104     * Disable following redirects.
105     *
106     * @return this builder
107     */
108    public GetBuilder disableRedirects() {
109        request.setConfig(RequestConfig.custom().setRedirectsEnabled(false).build());
110        return this;
111    }
112
113    /**
114     * Set the prefer header for this request to representation, to indicate that links to other resources and their
115     * properties should also be included.
116     * 
117     * @return this builder
118     */
119    public GetBuilder preferRepresentation() {
120        request.setHeader(PREFER, buildPrefer("representation", null, null));
121        return this;
122    }
123
124    /**
125     * Set the prefer header for this request to representation, to indicate that links to other resources and their
126     * properties should also be included. The set of properties returned can be further specified by providing lists
127     * of LDP defined preferences to omit or include.
128     * 
129     * @param includeUris URIs of LDP defined preferences to include
130     * @param omitUris URIs of LDP defined preferences to omit
131     * @return this builder
132     */
133    public GetBuilder preferRepresentation(final List<URI> includeUris, final List<URI> omitUris) {
134        request.setHeader(PREFER, buildPrefer("representation", includeUris, omitUris));
135        return this;
136    }
137
138    private String buildPrefer(final String prefer, final List<URI> includeUris, final List<URI> omitUris) {
139        final StringJoiner preferJoin = new StringJoiner("; ");
140        preferJoin.add("return=" + prefer);
141
142        if (includeUris != null) {
143            final String include = includeUris.stream().map(URI::toString).collect(Collectors.joining(" "));
144            if (include.length() > 0) {
145                preferJoin.add("include=\"" + include + "\"");
146            }
147        }
148
149        if (omitUris != null) {
150            final String omit = omitUris.stream().map(URI::toString).collect(Collectors.joining(" "));
151            if (omit.length() > 0) {
152                preferJoin.add("omit=\"" + omit + "\"");
153            }
154        }
155
156        return preferJoin.toString();
157    }
158
159    /**
160     * Provide an etag for the if-none-match header for this request
161     * 
162     * @param etag etag to provide as the if-none-match header
163     * @return this builder
164     */
165    public GetBuilder ifNoneMatch(final String etag) {
166        if (etag != null) {
167            request.setHeader(IF_NONE_MATCH, etag);
168        }
169        return this;
170    }
171
172    /**
173     * Provide a if-last-modified header for this request
174     * 
175     * @param lastModified date to provided as the if-modified-since header
176     * @return this builder
177     */
178    public GetBuilder ifModifiedSince(final String lastModified) {
179        if (lastModified != null) {
180            request.setHeader(IF_MODIFIED_SINCE, lastModified);
181        }
182        return this;
183    }
184}