/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.jena.atlas.web ;
import static org.apache.jena.atlas.lib.Lib.hashCodeObject ;
import java.util.LinkedHashMap ;
import java.util.Map ;
import java.util.Objects;
import org.slf4j.Logger ;
import org.slf4j.LoggerFactory ;
/**
* A structure to represent a <a
* href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.7">media
* type</a>. See also the <a
* href="http://httpd.apache.org/docs/current/content-negotiation.html">Apache
* httpd documentation</a>.
*/
public class MediaType {
private static Logger log = LoggerFactory.getLogger(MediaType.class) ;
private static final String strCharset = "charset" ;
private final String type ;
private final String subType ;
// Keys in insertion order.
private final Map<String, String> params ;
protected MediaType(ParsedMediaType parser) {
this.type = parser.type ;
this.subType = parser.subType ;
this.params = parser.params ;
}
public MediaType(MediaType other) {
this.type = other.type ;
this.subType = other.subType ;
// Order preserving copy.
this.params = new LinkedHashMap<>(other.params) ;
}
/** Create a media type from type and subType */
public MediaType(String type, String subType) {
this(type, subType, null) ;
}
/** Create a media type from type and subType */
public MediaType(String type, String subType, String charset) {
this.type = type ;
this.subType = subType ;
this.params = new LinkedHashMap<>() ;
if ( charset != null )
setParameter(strCharset, charset) ;
}
public static MediaType create(String contentType, String charset) {
ParsedMediaType mediaType = parse(contentType) ;
if ( charset != null )
mediaType.params.put(strCharset, charset) ;
return new MediaType(mediaType) ;
}
public static MediaType createFromContentType(String string) {
return new MediaType(parse(string)) ;
}
public static MediaType create(String contentType, String subType, String charset) {
return new MediaType(contentType, subType, charset) ;
}
public static MediaType create(String string) {
if ( string == null )
return null ;
return new MediaType(parse(string)) ;
}
public static ParsedMediaType parse(String string) {
ParsedMediaType mt = new ParsedMediaType() ;
String[] x = WebLib.split(string, ";") ;
String[] t = WebLib.split(x[0], "/") ;
mt.type = t[0] ;
if ( t.length > 1 )
mt.subType = t[1] ;
for (int i = 1; i < x.length; i++) {
// Each a parameter
String z[] = WebLib.split(x[i], "=") ;
if ( z.length == 2 )
mt.params.put(z[0], z[1]) ;
else
log.warn("Duff parameter: " + x[i] + " in " + string) ;
}
return mt ;
}
/** Format for use in HTTP header */
public String toHeaderString() {
StringBuilder b = new StringBuilder() ;
b.append(type) ;
if ( subType != null )
b.append("/").append(subType) ;
for (Map.Entry<String, String> entry : params.entrySet()) {
b.append(";") ;
b.append(entry.getKey()) ;
b.append("=") ;
b.append(entry.getValue()) ;
}
return b.toString() ;
}
/**
* Format to show structure - intentionally different from header form so
* you can tell parsing happened correctly
*/
@Override
public String toString() {
StringBuilder b = new StringBuilder() ;
b.append("[") ;
b.append(type) ;
if ( subType != null )
b.append("/").append(subType) ;
for (String k : params.keySet()) {
if ( k.equals("boundary") )
continue ;
String v = params.get(k) ;
b.append(" ") ;
b.append(k) ;
b.append("=") ;
b.append(v) ;
}
b.append("]") ;
return b.toString() ;
}
// private String type = null ;
// private String subType = null ;
// // Keys in insertion order.
// private Map<String, String> params = new LinkedHashMap<String, String>()
// ;
@Override
public int hashCode() {
return hashCodeObject(type, 1) ^ hashCodeObject(subType, 2) ^ hashCodeObject(params, 3) ;
}
@Override
public boolean equals(Object object) {
if ( this == object )
return true ;
if ( !(object instanceof MediaType) )
return false ;
MediaType mt = (MediaType)object ;
return Objects.equals(type, mt.type) && Objects.equals(subType, mt.subType) && Objects.equals(params, mt.params) ;
}
public String getParameter(String name) {
return params.get(name) ;
}
private void setParameter(String name, String value) {
params.put(name, value) ;
strContentType = null ;
}
// A cache.
private String strContentType = null ;
public String getContentType() {
if ( strContentType != null )
return strContentType ;
if ( subType == null )
return type ;
return type + "/" + subType ;
}
public String getCharset() {
return getParameter(strCharset) ;
}
public String getSubType() {
return subType ;
}
// public void setSubType(String subType) { this.subType = subType ;
// strContentType = null ; }
public String getType() {
return type ;
}
// public void setType(String type) { this.type = type ; strContentType =
// null ; }
/**
* The outcome of parsing
*
* @see MediaType#parse
*/
/* package */static class ParsedMediaType {
public String type ;
public String subType ;
public Map<String, String> params = new LinkedHashMap<>() ;
}
}