/* * Copyright 2015 Petr Bouda * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied * See the License for the specific language governing permissions and * limitations under the License. */ package org.joyrest.model.http; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.StringTokenizer; import static org.joyrest.exception.type.RestException.internalServerErrorSupplier; import static java.util.Objects.isNull; import static java.util.Objects.nonNull; import static java.util.Objects.requireNonNull; import static java.util.stream.Collectors.toList; import static java.util.stream.Collectors.toMap; public final class MediaType { public static final MediaType FORM_URLENCODED = new MediaType("application", "x-www-form-urlencoded"); public static final MediaType JSON = new MediaType("application", "json"); public static final MediaType OCTET_STREAM = new MediaType("application", "octet-stream"); public static final MediaType MULTIPART_FORM_DATA = new MediaType("multipart", "form-data"); public static final MediaType HTML = new MediaType("text", "html"); public static final MediaType PLAIN_TEXT = new MediaType("text", "plain"); public static final MediaType WILDCARD = new MediaType("*", "*"); public static final MediaType TEXT_XML = new MediaType("text", "xml"); public static final MediaType XML = new MediaType("application", "xml"); public static final MediaType ATOM_XML = new MediaType("application", "atom+xml", XML); public static final MediaType XHTML_XML = new MediaType("application", "xhtml+xml", XML); public static final MediaType SERIALIZATION_JAVA = new MediaType("serialization", "java"); public static final MediaType SERIALIZATION_HESSIAN = new MediaType("serialization", "hessian"); private static final Map<String, MediaType> BASIC_MEDIA_TYPE; static { BASIC_MEDIA_TYPE = new HashMap<>(); BASIC_MEDIA_TYPE.put("json", JSON); BASIC_MEDIA_TYPE.put("xml", XML); } private final String type; private final String subType; private Optional<MediaType> processingType; private Map<String, String> params; private MediaType(String type, String subType) { this(type, subType, null); } private MediaType(String type, String subType, MediaType processingType) { this(type, subType, processingType, null); } private MediaType(String type, String subType, MediaType processingType, Map<String, String> params) { this.type = type; this.subType = subType; this.processingType = Optional.ofNullable(processingType); this.params = nonNull(params) ? params : new HashMap<>(); } public static List<MediaType> list(String mediaTypes) { return split(mediaTypes, ",").stream() .distinct() .map(MediaType::of) .collect(toList()); } public static MediaType of(String mediaType) { if (isNull(mediaType) || mediaType.isEmpty()) { return WILDCARD; } // Contains header some params? Map<String, String> params = new HashMap<>(); if (mediaType.contains(";")) { List<String> mediaTypes = split(mediaType, ";"); mediaType = mediaTypes.get(0); params = mediaTypes.stream() .skip(1) .filter(param -> param.contains("=")) .map(MediaType::paramToNameValue) .collect(toMap(NameValueEntity::getName, NameValueEntity::getValue)); } List<String> typeSplit = split(mediaType, "/"); if (typeSplit.size() != 2) { throw internalServerErrorSupplier( String.format("Invalid MediaType [%s]", mediaType)).get(); } MediaType processingType = null; if (typeSplit.get(1).contains("+")) { List<String> processingSplit = split(typeSplit.get(1), "+"); if (processingSplit.size() == 2) { processingType = BASIC_MEDIA_TYPE.get(processingSplit.get(1)); } } return new MediaType(typeSplit.get(0), typeSplit.get(1), processingType, params); } private static List<String> split(String value, String delimiter) { List<String> result = new ArrayList<>(); StringTokenizer tokenizer = new StringTokenizer(value, delimiter); while (tokenizer.hasMoreTokens()) { String trimmed = tokenizer.nextToken().trim(); if (!trimmed.isEmpty()) { result.add(trimmed); } } return result; } private static NameValueEntity<String, String> paramToNameValue(String param) { char[] chars = param.toCharArray(); int index = param.indexOf("="); String name = new String(chars, 0, index); String value = new String(chars, index + 1, chars.length - index - 1); return new NameValueEntity<>(name.toLowerCase(), value.toLowerCase()); } public String get() { return type + "/" + subType; } public String getType() { return type; } public String getSubType() { return subType; } public Optional<MediaType> getProcessingType() { return processingType; } public Map<String, String> getParams() { return params; } public Optional<String> getParam(String name) { requireNonNull(name, "Param name cannot be null."); return Optional.ofNullable(params.get(name.toLowerCase())); } @Override public int hashCode() { return Objects.hash(type, subType); } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null || getClass() != obj.getClass()) { return false; } final MediaType other = (MediaType) obj; return Objects.equals(this.type, other.type) && Objects.equals(this.subType, other.subType); } @Override public String toString() { return type + "/" + subType; } }