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