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

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

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

import static java.nio.charset.StandardCharsets.ISO_8859_1;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.time.format.DateTimeFormatter.RFC_1123_DATE_TIME;

import java.io.ByteArrayOutputStream;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.time.ZonedDateTime;
import java.time.format.DateTimeParseException;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

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

public final class HttpHeaderContentDisposition {

    private static final String INVALID_HEADER_FIELD_PARAMETER_FORMAT =
        "Invalid header field parameter format (as defined in RFC 5987)";

    private final String type;

    private final String name;

    private final String filename;

    private final Charset charset;

    private final Long size;

    private final ZonedDateTime creationDate;

    private final ZonedDateTime modificationDate;

    private final ZonedDateTime readDate;

    private HttpHeaderContentDisposition(String type, String name, String filename,
            Charset charset, Long size, ZonedDateTime creationDate,
            ZonedDateTime modificationDate, ZonedDateTime readDate) {
        this.type = type;
        this.name = name;
        this.filename = filename;
        this.charset = charset;
        this.size = size;
        this.creationDate = creationDate;
        this.modificationDate = modificationDate;
        this.readDate = readDate;
    }

    public String getType() {
        return this.type;
    }

    public String getName() {
        return this.name;
    }

    public String getFilename() {
        return this.filename;
    }

    public Charset getCharset() {
        return this.charset;
    }

    public Long getSize() {
        return this.size;
    }

    public ZonedDateTime getCreationDate() {
        return this.creationDate;
    }

    public ZonedDateTime getModificationDate() {
        return this.modificationDate;
    }

    public ZonedDateTime getReadDate() {
        return this.readDate;
    }

    @Override
    public boolean equals(Object other) {
        if (this == other) {
            return true;
        }
        if (!(other instanceof HttpHeaderContentDisposition)) {
            return false;
        }
        HttpHeaderContentDisposition otherCd = (HttpHeaderContentDisposition) other;
        return (ObjectUtil.equals(this.type, otherCd.type) &&
                ObjectUtil.equals(this.name, otherCd.name) &&
                ObjectUtil.equals(this.filename, otherCd.filename) &&
                ObjectUtil.equals(this.charset, otherCd.charset) &&
                ObjectUtil.equals(this.size, otherCd.size) &&
                ObjectUtil.equals(this.creationDate, otherCd.creationDate)&&
                ObjectUtil.equals(this.modificationDate, otherCd.modificationDate)&&
                ObjectUtil.equals(this.readDate, otherCd.readDate));
    }

    @Override
    public int hashCode() {
        int result = ObjectUtil.hashCode(this.type);
        result = 31 * result + ObjectUtil.hashCode(this.name);
        result = 31 * result + ObjectUtil.hashCode(this.filename);
        result = 31 * result + ObjectUtil.hashCode(this.charset);
        result = 31 * result + ObjectUtil.hashCode(this.size);
        result = 31 * result + (this.creationDate != null ? this.creationDate.hashCode() : 0);
        result = 31 * result + (this.modificationDate != null ? this.modificationDate.hashCode() : 0);
        result = 31 * result + (this.readDate != null ? this.readDate.hashCode() : 0);
        return result;
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        if (this.type != null) {
            sb.append(this.type);
        }
        if (this.name != null) {
            sb.append("; name=\"");
            sb.append(this.name).append('\"');
        }
        if (this.filename != null) {
            if (this.charset == null || StandardCharsets.US_ASCII.equals(this.charset)) {
                sb.append("; filename=\"");
                sb.append(escapeQuotationsInFilename(this.filename)).append('\"');
            }
            else {
                sb.append("; filename*=");
                sb.append(encodeFilename(this.filename, this.charset));
            }
        }
        if (this.size != null) {
            sb.append("; size=");
            sb.append(this.size);
        }
        if (this.creationDate != null) {
            sb.append("; creation-date=\"");
            sb.append(RFC_1123_DATE_TIME.format(this.creationDate));
            sb.append('\"');
        }
        if (this.modificationDate != null) {
            sb.append("; modification-date=\"");
            sb.append(RFC_1123_DATE_TIME.format(this.modificationDate));
            sb.append('\"');
        }
        if (this.readDate != null) {
            sb.append("; read-date=\"");
            sb.append(RFC_1123_DATE_TIME.format(this.readDate));
            sb.append('\"');
        }
        return sb.toString();
    }

    public static Builder builder(String type) {
        return new BuilderImpl(type);
    }

    public static HttpHeaderContentDisposition empty() {
        return new HttpHeaderContentDisposition("", null, null, null, null, null, null, null);
    }

    public static HttpHeaderContentDisposition parse(String contentDisposition) {
        List parts = tokenize(contentDisposition);
        String type = parts.get(0);
        String name = null;
        String filename = null;
        Charset charset = null;
        Long size = null;
        ZonedDateTime creationDate = null;
        ZonedDateTime modificationDate = null;
        ZonedDateTime readDate = null;
        for (int i = 1; i < parts.size(); i++) {
            String part = parts.get(i);
            int eqIndex = part.indexOf('=');
            if (eqIndex != -1) {
                String attribute = part.substring(0, eqIndex);
                String value = (part.startsWith("\"", eqIndex + 1) && part.endsWith("\"") ?
                        part.substring(eqIndex + 2, part.length() - 1) :
                        part.substring(eqIndex + 1));
                if (attribute.equals("name") ) {
                    name = value;
                }
                else if (attribute.equals("filename*") ) {
                    int idx1 = value.indexOf('\'');
                    int idx2 = value.indexOf('\'', idx1 + 1);
                    if (idx1 != -1 && idx2 != -1) {
                        charset = Charset.forName(value.substring(0, idx1).trim());
                        if (!(UTF_8.equals(charset) || ISO_8859_1.equals(charset))) {
                            throw new IllegalArgumentException("Charset should be UTF-8 or ISO-8859-1");
                        }
                        filename = decodeFilename(value.substring(idx2 + 1), charset);
                    }  else {
                        // US ASCII
                        filename = decodeFilename(value, StandardCharsets.US_ASCII);
                    }
                } else if (attribute.equals("filename") && (filename == null)) {
                    filename = value;
                } else if (attribute.equals("size") ) {
                    size = Long.parseLong(value);
                } else if (attribute.equals("creation-date")) {
                    try {
                        creationDate = ZonedDateTime.parse(value, RFC_1123_DATE_TIME);
                    }
                    catch (DateTimeParseException ex) {
                        // ignore
                    }
                } else if (attribute.equals("modification-date")) {
                    try {
                        modificationDate = ZonedDateTime.parse(value, RFC_1123_DATE_TIME);
                    }
                    catch (DateTimeParseException ex) {
                        // ignore
                    }
                } else if (attribute.equals("read-date")) {
                    try {
                        readDate = ZonedDateTime.parse(value, RFC_1123_DATE_TIME);
                    }
                    catch (DateTimeParseException ex) {
                        // ignore
                    }
                }
            } else {
                throw new IllegalArgumentException("Invalid content disposition format");
            }
        }
        return new HttpHeaderContentDisposition(type, name, filename, charset, size, creationDate, modificationDate, readDate);
    }

    private static List tokenize(String headerValue) {
        int index = headerValue.indexOf(';');
        String type = (index >= 0 ? headerValue.substring(0, index) : headerValue).trim();
        if (type.isEmpty()) {
            throw new IllegalArgumentException("Content-Disposition header must not be empty");
        }
        List parts = new ArrayList<>();
        parts.add(type);
        if (index >= 0) {
            do {
                int nextIndex = index + 1;
                boolean quoted = false;
                boolean escaped = false;
                while (nextIndex < headerValue.length()) {
                    char ch = headerValue.charAt(nextIndex);
                    if (ch == ';') {
                        if (!quoted) {
                            break;
                        }
                    }
                    else if (!escaped && ch == '"') {
                        quoted = !quoted;
                    }
                    escaped = (!escaped && ch == '\\');
                    nextIndex++;
                }
                String part = headerValue.substring(index + 1, nextIndex).trim();
                if (!part.isEmpty()) {
                    parts.add(part);
                }
                index = nextIndex;
            }
            while (index < headerValue.length());
        }
        return parts;
    }

    private static String decodeFilename(String filename, Charset charset) {
        Objects.requireNonNull(filename, "'input' String` should not be null");
        Objects.requireNonNull(charset, "'charset' should not be null");
        byte[] value = filename.getBytes(charset);
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        int index = 0;
        while (index < value.length) {
            byte b = value[index];
            if (isRFC5987AttrChar(b)) {
                baos.write((char) b);
                index++;
            }
            else if (b == '%' && index < value.length - 2) {
                char[] array = new char[]{(char) value[index + 1], (char) value[index + 2]};
                try {
                    baos.write(Integer.parseInt(String.valueOf(array), 16));
                }
                catch (NumberFormatException ex) {
                    throw new IllegalArgumentException(INVALID_HEADER_FIELD_PARAMETER_FORMAT, ex);
                }
                index+=3;
            }
            else {
                throw new IllegalArgumentException(INVALID_HEADER_FIELD_PARAMETER_FORMAT);
            }
        }
        return new String(baos.toByteArray(), charset);
    }

    private static boolean isRFC5987AttrChar(byte c) {
        return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
                c == '!' || c == '#' || c == '$' || c == '&' || c == '+' || c == '-' ||
                c == '.' || c == '^' || c == '_' || c == '`' || c == '|' || c == '~';
    }

    private static String escapeQuotationsInFilename(String filename) {
        if (filename.indexOf('"') == -1 && filename.indexOf('\\') == -1) {
            return filename;
        }
        boolean escaped = false;
        StringBuilder sb = new StringBuilder();
        for (char c : filename.toCharArray()) {
            sb.append((c == '"' && !escaped) ? "\\\"" : c);
            escaped = (!escaped && c == '\\');
        }
        // Remove backslash at the end..
        if (escaped) {
            sb.deleteCharAt(sb.length() - 1);
        }
        return sb.toString();
    }

    private static String encodeFilename(String input, Charset charset) {
        Objects.requireNonNull(input, "`input` is required");
        Objects.requireNonNull(charset, "`charset` is required");
        if (StandardCharsets.US_ASCII.equals(charset)) {
            throw new IllegalArgumentException("ASCII does not require encoding");
        }
        if (!(UTF_8.equals(charset) || ISO_8859_1.equals(charset))) {
            throw new IllegalArgumentException("Only UTF-8 and ISO-8859-1 supported.");
        }
        byte[] source = input.getBytes(charset);
        int len = source.length;
        StringBuilder sb = new StringBuilder(len << 1);
        sb.append(charset.name());
        sb.append("''");
        for (byte b : source) {
            if (isRFC5987AttrChar(b)) {
                sb.append((char) b);
            } else {
                sb.append('%');
                char hex1 = Character.toUpperCase(Character.forDigit((b >> 4) & 0xF, 16));
                char hex2 = Character.toUpperCase(Character.forDigit(b & 0xF, 16));
                sb.append(hex1);
                sb.append(hex2);
            }
        }
        return sb.toString();
    }

    public interface Builder {

        Builder name(String name);

        Builder filename(String filename);

        Builder filename(String filename, Charset charset);

        Builder size(Long size);

        Builder creationDate(ZonedDateTime creationDate);

        Builder modificationDate(ZonedDateTime modificationDate);

        Builder readDate(ZonedDateTime readDate);

        HttpHeaderContentDisposition build();
    }


    private static class BuilderImpl implements Builder {

        private final String type;

        private String name;

        private String filename;

        private Charset charset;

        private Long size;

        private ZonedDateTime creationDate;

        private ZonedDateTime modificationDate;

        private ZonedDateTime readDate;

        public BuilderImpl(String type) {
            if (null == type || type.isEmpty()) {
                throw new IllegalArgumentException("'type' must not be not empty");
            }
            this.type = type;
        }

        @Override
        public Builder name(String name) {
            this.name = name;
            return this;
        }

        @Override
        public Builder filename(String filename) {
            if (null == filename || filename.isEmpty()) {
                throw new IllegalArgumentException("No filename");
            }
            this.filename = filename;
            return this;
        }

        @Override
        public Builder filename(String filename, Charset charset) {
            if (null == filename || filename.isEmpty()) {
                throw new IllegalArgumentException("No filename");
            }
            this.filename = filename;
            this.charset = charset;
            return this;
        }

        @Override
        public Builder size(Long size) {
            this.size = size;
            return this;
        }

        @Override
        public Builder creationDate(ZonedDateTime creationDate) {
            this.creationDate = creationDate;
            return this;
        }

        @Override
        public Builder modificationDate(ZonedDateTime modificationDate) {
            this.modificationDate = modificationDate;
            return this;
        }

        @Override
        public Builder readDate(ZonedDateTime readDate) {
            this.readDate = readDate;
            return this;
        }

        @Override
        public HttpHeaderContentDisposition build() {
            return new HttpHeaderContentDisposition(this.type, this.name, this.filename, this.charset,
                    this.size, this.creationDate, this.modificationDate, this.readDate);
        }
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy