All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.zodiac.sdk.nio.http.common.HttpHeaderRange Maven / Gradle / Ivy

There is a newer version: 1.6.8
Show newest version
package org.zodiac.sdk.nio.http.common;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.StringJoiner;

import org.zodiac.sdk.toolkit.util.ObjectUtil;
import org.zodiac.sdk.toolkit.util.StringUtil;

public abstract class HttpHeaderRange {

    private static final int MAX_RANGES = 100;

    private static final String BYTE_RANGE_PREFIX = "bytes=";

    public HttpHeaderRange() {
    }

    public abstract long getRangeStart(long length);

    public abstract long getRangeEnd(long length);

    public static HttpHeaderRange createByteRange(long firstBytePos) {
        return new ByteRange(firstBytePos, null);
    }

    public static HttpHeaderRange createByteRange(long firstBytePos, long lastBytePos) {
        return new ByteRange(firstBytePos, lastBytePos);
    }

    public static HttpHeaderRange createSuffixRange(long suffixLength) {
        return new SuffixByteRange(suffixLength);
    }

    public static List parseRanges(String ranges) {
        if (StringUtil.isEmpty(ranges)) {
            return Collections.emptyList();
        }
        if (!ranges.startsWith(BYTE_RANGE_PREFIX)) {
            throw new IllegalArgumentException("Range '" + ranges + "' does not start with 'bytes='");
        }
        ranges = ranges.substring(BYTE_RANGE_PREFIX.length());

        String[] tokens = StringUtil.toStringArray(ranges, ",");
        if (tokens.length > MAX_RANGES) {
            throw new IllegalArgumentException("Too many ranges: " + tokens.length);
        }
        List result = new ArrayList<>(tokens.length);
        for (String token : tokens) {
            result.add(parseRange(token));
        }
        return result;
    }

    private static HttpHeaderRange parseRange(String range) {
        if (null == range || range.length() < 0) {
            throw new IllegalArgumentException("Range String must not be empty");
        }
        int dashIdx = range.indexOf('-');
        if (dashIdx > 0) {
            long firstPos = Long.parseLong(range.substring(0, dashIdx));
            if (dashIdx < range.length() - 1) {
                Long lastPos = Long.parseLong(range.substring(dashIdx + 1));
                return new ByteRange(firstPos, lastPos);
            } else {
                return new ByteRange(firstPos, null);
            }
        } else if (dashIdx == 0) {
            long suffixLength = Long.parseLong(range.substring(1));
            return new SuffixByteRange(suffixLength);
        } else {
            throw new IllegalArgumentException("Range '" + range + "' does not contain \"-\"");
        }
    }

    public static String toString(Collection ranges) {
        if (null == ranges|| ranges.isEmpty()) {
            throw new IllegalArgumentException("Ranges Collection must not be empty");
        }
        StringJoiner builder = new StringJoiner(", ", BYTE_RANGE_PREFIX, "");
        for (HttpHeaderRange range : ranges) {
            builder.add(range.toString());
        }
        return builder.toString();
    }

    private static class ByteRange extends HttpHeaderRange {

        private final long firstPos;

        private final Long lastPos;

        public ByteRange(long firstPos, Long lastPos) {
            assertPositions(firstPos, lastPos);
            this.firstPos = firstPos;
            this.lastPos = lastPos;
        }

        private void assertPositions(long firstBytePos, Long lastBytePos) {
            if (firstBytePos < 0) {
                throw new IllegalArgumentException("Invalid first byte position: " + firstBytePos);
            }
            if (lastBytePos != null && lastBytePos < firstBytePos) {
                throw new IllegalArgumentException("firstBytePosition=" + firstBytePos +
                        " should be less then or equal to lastBytePosition=" + lastBytePos);
            }
        }

        @Override
        public long getRangeStart(long length) {
            return this.firstPos;
        }

        @Override
        public long getRangeEnd(long length) {
            if (this.lastPos != null && this.lastPos < length) {
                return this.lastPos;
            }
            else {
                return length - 1;
            }
        }

        @Override
        public boolean equals(Object other) {
            if (this == other) {
                return true;
            }
            if (!(other instanceof ByteRange)) {
                return false;
            }
            ByteRange otherRange = (ByteRange) other;
            return (this.firstPos == otherRange.firstPos &&
                    ObjectUtil.equals(this.lastPos, otherRange.lastPos));
        }

        @Override
        public int hashCode() {
            return (ObjectUtil.hashCode(this.firstPos) * 31 + ObjectUtil.hashCode(this.lastPos));
        }

        @Override
        public String toString() {
            StringBuilder builder = new StringBuilder();
            builder.append(this.firstPos);
            builder.append('-');
            if (this.lastPos != null) {
                builder.append(this.lastPos);
            }
            return builder.toString();
        }
    }


    /**
     * Represents an HTTP/1.1 suffix byte range, with a number of suffix bytes.
     * @see Byte Ranges
     * @see HttpHeaderRange#createSuffixRange(long)
     */
    private static class SuffixByteRange extends HttpHeaderRange {

        private final long suffixLength;

        public SuffixByteRange(long suffixLength) {
            if (suffixLength < 0) {
                throw new IllegalArgumentException("Invalid suffix length: " + suffixLength);
            }
            this.suffixLength = suffixLength;
        }

        @Override
        public long getRangeStart(long length) {
            if (this.suffixLength < length) {
                return length - this.suffixLength;
            }
            else {
                return 0;
            }
        }

        @Override
        public long getRangeEnd(long length) {
            return length - 1;
        }

        @Override
        public boolean equals(Object other) {
            if (this == other) {
                return true;
            }
            if (!(other instanceof SuffixByteRange)) {
                return false;
            }
            SuffixByteRange otherRange = (SuffixByteRange) other;
            return (this.suffixLength == otherRange.suffixLength);
        }

        @Override
        public int hashCode() {
            return Long.hashCode(this.suffixLength);
        }

        @Override
        public String toString() {
            return "-" + this.suffixLength;
        }
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy