// MimeType.java
// $Id$
// (c) COPYRIGHT MIT and INRIA, 1996.
// Please first read the full copyright statement in file COPYRIGHT.html
package org.w3c.www.mime;
import java.io.Serializable;
import java.util.ArrayList;
/**
* This class is used to represent parsed MIME types.
* It creates this representation from a string based representation of
* the MIME type, as defined in the RFC 1345.
*/
public class MimeType implements Serializable, Cloneable {
/**
* List of well known MIME types:
*/
public static MimeType TEXT_HTML = null;
public static MimeType APPLICATION_POSTSCRIPT = null;
public static MimeType TEXT_PLAIN = null;
public static MimeType APPLICATION_X_WWW_FORM_URLENCODED = null;
public static MimeType APPLICATION_OCTET_STREAM = null;
public static MimeType MULTIPART_FORM_DATA = null;
public static MimeType APPLICATION_X_JAVA_AGENT = null;
public static MimeType MESSAGE_HTTP = null;
public static MimeType TEXT_CSS = null;
public static MimeType TEXT_XML = null;
public static MimeType APPLICATION_XML = null;
public static MimeType TEXT = null;
public static MimeType APPLICATION_RDF_XML = null;
public static MimeType APPLICATION_XHTML_XML = null;
public static String star = "*".intern();
static {
try {
TEXT_HTML
= new MimeType("text/html");
APPLICATION_POSTSCRIPT
= new MimeType("application/postscript");
TEXT_PLAIN
= new MimeType("text/plain");
APPLICATION_X_WWW_FORM_URLENCODED
= new MimeType("application/x-www-form-urlencoded");
APPLICATION_OCTET_STREAM
= new MimeType("application/octet-stream");
MULTIPART_FORM_DATA
= new MimeType("multipart/form-data");
APPLICATION_X_JAVA_AGENT
= new MimeType("application/x-java-agent");
MESSAGE_HTTP
= new MimeType("message/http");
TEXT_CSS
= new MimeType("text/css");
TEXT_XML
= new MimeType("text/xml");
TEXT
= new MimeType("text/*");
APPLICATION_RDF_XML
= new MimeType("application/rdf+xml");
APPLICATION_XHTML_XML
= new MimeType("application/xhtml+xml");
APPLICATION_XML
= new MimeType("application/xml");
} catch (MimeTypeFormatException e) {
System.out.println("httpd.MimeType: invalid static init.");
System.exit(1);
}
}
public final static int NO_MATCH = -1;
public final static int MATCH_TYPE = 1;
public final static int MATCH_SPECIFIC_TYPE = 2;
public final static int MATCH_SUBTYPE = 3;
public final static int MATCH_SPECIFIC_SUBTYPE = 4;
/**
* String representation of type
*
* @serial
*/
protected String type = null;
/**
* String representation of subtype
*
* @serial
*/
protected String subtype = null;
/**
* parameter names
*
* @serial
*/
protected String pnames[] = null;
/**
* parameter values
*
* @serial
*/
protected String pvalues[] = null;
/**
* external form of this mime type
*
* @serial
*/
protected String external = null;
/**
* How good the given MimeType matches the receiver of the method ?
* This method returns a matching level among:
*
* - NO_MATCH
- Types not matching,
* - MATCH_TYPE
- Types match,
* - MATCH_SPECIFIC_TYPE
- Types match exactly,
* - MATCH_SUBTYPE
- Types match, subtypes matches too
* - MATCH_SPECIFIC_SUBTYPE
- Types match, subtypes matches exactly
*
* The matches are ranked from worst match to best match, a simple
* Max ( match[i], matched) will give the best match.
*
* @param other The other MimeType to match against ourself.
*/
public int match(MimeType other) {
int match = NO_MATCH;
// match types:
if ((type == star) || (other.type == star)) {
return MATCH_TYPE;
} else if (type != other.type) {
return NO_MATCH;
} else {
match = MATCH_SPECIFIC_TYPE;
}
// match subtypes:
if ((subtype == star) || (other.subtype == star)) {
match = MATCH_SUBTYPE;
} else if (subtype != other.subtype) {
return NO_MATCH;
} else {
match = MATCH_SPECIFIC_SUBTYPE;
}
return match;
}
/**
* Find out if mime types are equivalent, based on heuristics
* like text/xml <=> application/xml and other problems related
* to format that may have multiple mime types.
* Note that text/html and application/xhtml+xml are not exactly
* the same
*
* @param mtype, a MimeType
* @return a boolean, true if the two mime types are equivalent
*/
public boolean equiv(MimeType mtype) {
if (match(mtype) == MATCH_SPECIFIC_SUBTYPE) {
return true;
}
if ((match(TEXT_XML) == MATCH_SPECIFIC_SUBTYPE) ||
(match(APPLICATION_XML) == MATCH_SPECIFIC_SUBTYPE)) {
if ((mtype.match(TEXT_XML) == MATCH_SPECIFIC_SUBTYPE) ||
(mtype.match(APPLICATION_XML) == MATCH_SPECIFIC_SUBTYPE)) {
return true;
}
}
return false;
}
/**
* A printable representation of this MimeType.
* The printed representation is guaranteed to be parseable by the
* String constructor.
*/
public String toString() {
if (external == null) {
StringBuilder sb = new StringBuilder(type);
sb.append((char) '/');
sb.append(subtype);
if (pnames != null) {
for (int i = 0; i < pnames.length; i++) {
sb.append(';');
sb.append(pnames[i]);
if (pvalues[i] != null) {
sb.append('=');
sb.append(pvalues[i]);
}
}
}
external = sb.toString().intern();
}
return external;
}
/**
* Does this MIME type has some value for the given parameter ?
*
* @param name The parameter to check.
* @return True if this parameter has a value, false
* otherwise.
*/
public boolean hasParameter(String name) {
if (name != null) {
if (pnames != null) {
String lname = name.toLowerCase();
for (String pname : pnames) {
if (pname.equals(name))
return true;
}
}
}
return false;
}
/**
* Get the major type of this mime type.
*
* @return The major type, encoded as a String.
*/
public String getType() {
return type;
}
/**
* Get the minor type (subtype) of this mime type.
*
* @return The minor or subtype encoded as a String.
*/
public String getSubtype() {
return subtype;
}
/**
* Get a mime type parameter value.
*
* @param name The parameter whose value is to be returned.
* @return The parameter value, or null if not found.
*/
public String getParameterValue(String name) {
if (name != null) {
if (pnames != null) {
String lname = name.toLowerCase();
for (int i = 0; i < pnames.length; i++) {
if (pnames[i].equals(lname))
return pvalues[i];
}
}
}
return null;
}
/**
* adds some parameters to a MimeType
*
* @param param a String array of parameter names
* @param values the corresponding String array of values
*/
public void addParameters(String[] param, String[] values) {
// sanity check
if ((param == null) || (values == null) ||
(values.length != param.length))
return;
if (pnames == null) {
pnames = param;
pvalues = values;
} else {
String[] nparam = new String[pnames.length + param.length];
String[] nvalues = new String[pvalues.length + values.length];
System.arraycopy(pnames, 0, nparam, 0, pnames.length);
System.arraycopy(param, 0, nparam, pnames.length, param.length);
System.arraycopy(pvalues, 0, nvalues, 0, pvalues.length);
System.arraycopy(values, 0, nvalues, pvalues.length, values.length);
pnames = nparam;
pvalues = nvalues;
}
for (int i = 0; i < pnames.length; i++) {
pnames[i] = pnames[i].toLowerCase();
}
external = null;
}
/**
* get a clone of this object
*
* @return another cloned instance of MimeType
*/
public MimeType getClone() {
try {
return (MimeType) clone();
} catch (CloneNotSupportedException ex) {
// should never happen as we are Cloneable!
}
// never reached
return null;
}
/**
* adds a parameter to a MimeType
*
* @param param the parameter name, as a String
* @param value the parameter value, as a Sting
*/
public void addParameter(String param, String value) {
String[] p = new String[1];
String[] v = new String[1];
p[0] = param;
v[0] = value;
addParameters(p, v);
}
/**
* Set the parameter to a MimeType (replace old value if any).
*
* @param param the parameter name, as a String
* @param value the parameter value, as a Sting
*/
public void setParameter(String param, String value) {
if (pnames == null) {
addParameter(param, value);
} else {
String lparam = param.toLowerCase();
for (int i = 0; i < pnames.length; i++) {
if (pnames[i].equals(lparam)) {
pvalues[i] = value;
return;
}
}
addParameter(lparam, value);
}
}
/**
* Construct MimeType object for the given string.
* The string should be the representation of the type. This methods
* tries to be compliant with HTTP1.1, p 15, although it is not
* (because of quoted-text not being accepted).
* FIXME
*
* @return A MimeType object
* @throws MimeTypeFormatException if the string couldn't be parsed.
* @parameter spec A string representing a MimeType
*/
public MimeType(String spec)
throws MimeTypeFormatException {
int strl = spec.length();
int start = 0, look = -1;
// skip leading/trailing blanks:
while ((start < strl) && (spec.charAt(start)) <= ' ')
start++;
while ((strl > start) && (spec.charAt(strl - 1) <= ' '))
strl--;
// get the type:
StringBuilder sb = new StringBuilder();
while ((start < strl) && ((look = spec.charAt(start)) != '/')) {
sb.append(Character.toLowerCase((char) look));
start++;
}
if (look != '/')
throw new MimeTypeFormatException(spec);
this.type = sb.toString().intern();
// get the subtype:
start++;
sb.setLength(0);
while ((start < strl)
&& ((look = spec.charAt(start)) > ' ') && (look != ';')) {
sb.append(Character.toLowerCase((char) look));
start++;
}
this.subtype = sb.toString().intern();
// get parameters, if any:
while ((start < strl) && ((look = spec.charAt(start)) <= ' '))
start++;
if (start < strl) {
if (spec.charAt(start) != ';')
throw new MimeTypeFormatException(spec);
start++;
ArrayList vp = new ArrayList(4);
ArrayList vv = new ArrayList(4);
while (start < strl) {
while ((start < strl) && (spec.charAt(start) <= ' ')) start++;
// get parameter name:
sb.setLength(0);
while ((start < strl)
&& ((look = spec.charAt(start)) > ' ') && (look != '=')) {
sb.append(Character.toLowerCase((char) look));
start++;
}
String name = sb.toString();
// get the value:
while ((start < strl) && (spec.charAt(start) <= ' ')) start++;
if (spec.charAt(start) != '=')
throw new MimeTypeFormatException(spec);
start++;
while ((start < strl) &&
((spec.charAt(start) == '"') ||
(spec.charAt(start) <= ' '))) start++;
sb.setLength(0);
while ((start < strl)
&& ((look = spec.charAt(start)) > ' ')
&& (look != ';')
&& (look != '"')) {
sb.append((char) look);
start++;
}
while ((start < strl) && (spec.charAt(start) != ';')) start++;
start++;
String value = sb.toString();
vp.add(name);
vv.add(value);
}
this.pnames = vp.toArray(new String[vp.size()]);
this.pvalues = vv.toArray(new String[vv.size()]);
}
}
public MimeType(String type, String subtype
, String pnames[], String pvalues[]) {
this.type = type.toLowerCase().intern();
this.subtype = subtype.toLowerCase().intern();
this.pnames = pnames;
this.pvalues = pvalues;
}
public MimeType(String type, String subtype) {
this.type = type.toLowerCase().intern();
this.subtype = subtype.toLowerCase().intern();
}
public static void main(String args[]) {
if (args.length == 1) {
MimeType type = null;
try {
type = new MimeType(args[0]);
} catch (MimeTypeFormatException e) {
}
if (type != null) {
System.out.println(type);
if (type.getClone().match(type) == MATCH_SPECIFIC_SUBTYPE) {
System.out.println("Clone OK");
} else {
System.out.println("Cloning failed");
}
} else {
System.out.println("Invalid mime type specification.");
}
} else {
System.out.println("Usage: java MimeType ");
}
}
}