001/**
002 * Copyright 2014 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.http.commons.domain;
018
019import static java.lang.Long.parseLong;
020import static java.util.regex.Pattern.compile;
021
022import java.util.regex.Matcher;
023import java.util.regex.Pattern;
024
025/**
026 * Range header parsing logic
027 *
028 * @author awoods
029 */
030public class Range {
031
032    private final long start;
033
034    private final long end;
035
036    private static Pattern rangePattern =
037        compile("^bytes\\s*=\\s*(\\d*)\\s*-\\s*(\\d*)");
038
039    /**
040     * Unbounded Range
041     */
042    public Range() {
043        this(0, -1);
044    }
045
046    /**
047     * Left-bounded range
048     * @param start
049     */
050    public Range(final long start) {
051        this(start, -1L);
052    }
053
054    /**
055     * Left and right bounded range
056     * @param start
057     * @param end
058     */
059    public Range(final long start, final long end) {
060        this.start = start;
061        this.end = end;
062    }
063
064    /**
065     * Does this range actually impose limits
066     * @return true if the range imposes limits
067     */
068    public boolean hasRange() {
069        return !(start == 0 && end == -1);
070    }
071
072    /**
073     * Length contained in the range
074     * @return length of the range
075     */
076    public long size() {
077        if (end == -1) {
078            return -1;
079        }
080        return end - start + 1;
081    }
082
083    /**
084     * Start of the range
085     * @return start of the range
086     */
087    public long start() {
088        return start;
089    }
090
091    /**
092     * End of the range
093     * @return end of the range
094     */
095    public long end() {
096        return end;
097    }
098
099    /**
100     * Convert an HTTP Range header to a Range object
101     * @param source
102     * @return range object
103     */
104    public static Range convert(final String source) {
105
106        final Matcher matcher = rangePattern.matcher(source);
107
108        if (!matcher.matches()) {
109            return new Range();
110        }
111
112        matcher.matches();
113        final String from = matcher.group(1);
114        final String to = matcher.group(2);
115
116        final long start;
117
118        if (from.equals("")) {
119            start = 0;
120        } else {
121            start = parseLong(from);
122        }
123
124        final long end;
125        if (to.equals("")) {
126            end = -1;
127        } else {
128            end = parseLong(to);
129        }
130
131        return new Range(start, end);
132    }
133}