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