/*
* JLibs: Common Utilities for Java
* Copyright (C) 2009 Santhosh Kumar T <santhosh.tekuri@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
package jlibs.nio.http.util;
import jlibs.nio.http.expr.Bean;
import jlibs.nio.http.expr.UnresolvedException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ThreadLocalRandom;
/**
* @author Santhosh Kumar Tekuri
*/
public class MediaType implements Bean{
public static final String CHARSET = "charset";
public static final String BOUNDARY = "boundary";
public static final MediaType WILDCARD = new MediaType("*", "*");
public static final MediaType TEXT_PLAIN = new MediaType("text", "plain");
public static final MediaType TEXT_XML = new MediaType("text", "xml");
public static final MediaType TEXT_HTML = new MediaType("text", "html");
public static final MediaType APPLICATION_OCTET_STREAM = new MediaType("application", "octet-stream");
public static final MediaType APPLICATION_XML = new MediaType("application", "xml");
public static final MediaType APPLICATION_ATOM_XML = new MediaType("application", "atom+xml");
public static final MediaType APPLICATION_XHTML_XML = new MediaType("application", "xhtml+xml");
public static final MediaType APPLICATION_SVG_XML = new MediaType("application", "svg+xml");
public static final MediaType APPLICATION_JSON = new MediaType("application", "json");
public static final MediaType APPLICATION_FORM_URLENCODED = new MediaType("application", "x-www-form-urlencoded");
public static final MediaType MULTIPART_FORM_DATA = new MediaType("multipart", "form-data");
public static final MediaType MULTIPART_MIXED = new MediaType("multipart", "mixed");
public static final MediaType MULTIPART_ALTERNATIVE = new MediaType("multipart", "alternative");
public static final MediaType SOAP_1_1 = TEXT_XML;
public static final MediaType SOAP_1_2 = new MediaType("application", "soap+xml");
public final String type;
public final String subType;
public final Map<String, String> params;
public MediaType(String type, String subType){
this(type, subType, null);
}
public MediaType(String type, String subType, Map<String, String> params){
this.type = type;
this.subType = subType;
validate();
this.params = params==null ? Collections.emptyMap() : Collections.unmodifiableMap(params);
}
public MediaType(String value){
this(new Parser(true, value));
}
public MediaType(Parser parser){
String name = parser.lvalue();
int slash = name.indexOf('/');
type = name.substring(0, slash);
subType = name.substring(slash+1);
validate();
parser.rvalue();
Map<String, String> params = null;
while(true){
String paramName = parser.lvalue();
if(paramName==null)
break;
if(params==null)
params = new HashMap<>();
params.put(paramName, parser.rvalue());
}
if(params==null)
this.params = Collections.emptyMap();
else
this.params = Collections.unmodifiableMap(params);
}
private void validate(){
Objects.requireNonNull(type, "type==null");
Objects.requireNonNull(subType, "subtype==null");
if("*".equals(type) && !"*".equals(subType))
throw new IllegalArgumentException("type==* && subType!=*");
}
public String getCharset(String defaultCharset){
return params.getOrDefault(CHARSET, defaultCharset);
}
public MediaType withCharset(String charset){
Map<String, String> params;
if(this.params.isEmpty())
params = Collections.singletonMap(CHARSET, charset);
else{
params = new HashMap<>(this.params);
params.put(CHARSET, charset);
}
return new MediaType(type, subType, params);
}
public boolean isMultipart(){
return type.equals(MULTIPART_FORM_DATA.type);
}
public String getBoundary(){
return params.get(BOUNDARY);
}
public MediaType withBoundary(String boundary){
Map<String, String> params;
if(this.params.isEmpty())
params = Collections.singletonMap(BOUNDARY, boundary);
else{
params = new HashMap<>(this.params);
params.put(BOUNDARY, boundary);
}
return new MediaType(type, subType, params);
}
public MediaType withBoundary(){
return withBoundary(Long.toHexString(ThreadLocalRandom.current().nextLong()).toLowerCase());
}
private boolean isCompatible(String str1, String str2){
return str1.equals(str2) || "*".equals(str1) || "*".equals(str2);
}
public boolean isCompatible(MediaType that){
if(that!=null && isCompatible(this.type, that.type)){
if(isCompatible(this.subType, that.subType))
return true;
int plus1 = this.subType.indexOf('+');
int plus2 = that.subType.indexOf('+');
if(plus1==-1 && plus2!=-1)
return isCompatible(this.subType, that.subType.substring(plus2+1));
else if(plus1!=-1 && plus2==-1)
return isCompatible(this.subType.substring(plus1+1), that.subType);
}
return false;
}
public boolean isAbstract(){
return "*".equals(type) || "*".equals(subType);
}
public boolean isAny(){
return "*".equals(type) && "*".equals(subType);
}
public boolean isText(){
return "text".equals(type);
}
public boolean isXML(){
return "xml".equals(subType) || subType.endsWith("+xml");
}
@Override
public boolean equals(Object obj){
if(obj==this)
return true;
if(obj instanceof MediaType){
MediaType that = (MediaType)obj;
return this.type.equalsIgnoreCase(that.type)
&& this.subType.equalsIgnoreCase(that.type)
&& this.params.equals(that.params);
}else
return false;
}
private String toString;
@Override
public String toString(){
if(toString==null){
StringBuilder builder = new StringBuilder();
builder.append(type).append('/').append(subType);
for(Map.Entry<String, String> entry: params.entrySet())
builder.append(';').append(entry.getKey()).append('=').append(entry.getValue());
toString = builder.toString();
}
return toString;
}
@Override
@SuppressWarnings("StringEquality")
public Object getField(String name) throws UnresolvedException{
if(name=="type")
return type;
else if(name=="subtype")
return subType;
else if(name=="charset")
return getCharset(null);
else if(name=="is_any")
return isAny();
else if(name=="is_xml")
return isXML();
else if(name=="is_multipart")
return isMultipart();
else if(name=="is_json")
return isCompatible(APPLICATION_JSON);
else if(name=="is_soap11")
return isCompatible(SOAP_1_1);
else if(name=="is_soap12")
return isCompatible(SOAP_1_2);
else if(name=="is_octet_stream")
return isCompatible(APPLICATION_OCTET_STREAM);
else if(name=="is_form_urlencoded")
return isCompatible(APPLICATION_FORM_URLENCODED);
else
throw new UnresolvedException(name);
}
}