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