001: // MimeType.java
002: // $Id: MimeType.java,v 1.39 2005/08/29 11:37:35 ylafon Exp $
003: // (c) COPYRIGHT MIT and INRIA, 1996.
004: // Please first read the full copyright statement in file COPYRIGHT.html
005:
006: package org.w3c.www.mime;
007:
008: import java.util.Vector;
009:
010: import java.io.PrintStream;
011: import java.io.Serializable;
012:
013: /**
014: * This class is used to represent parsed MIME types.
015: * It creates this representation from a string based representation of
016: * the MIME type, as defined in the RFC 1345.
017: */
018:
019: public class MimeType implements Serializable, Cloneable {
020: /**
021: * List of well known MIME types:
022: */
023: public static MimeType TEXT_HTML = null;
024: public static MimeType APPLICATION_POSTSCRIPT = null;
025: public static MimeType TEXT_PLAIN = null;
026: public static MimeType APPLICATION_X_WWW_FORM_URLENCODED = null;
027: public static MimeType APPLICATION_OCTET_STREAM = null;
028: public static MimeType MULTIPART_FORM_DATA = null;
029: public static MimeType APPLICATION_X_JAVA_AGENT = null;
030: public static MimeType MESSAGE_HTTP = null;
031: public static MimeType TEXT_CSS = null;
032: public static MimeType TEXT_XML = null;
033: public static MimeType APPLICATION_XML = null;
034: public static MimeType TEXT = null;
035: public static MimeType APPLICATION_RDF_XML = null;
036: public static MimeType APPLICATION_XHTML_XML = null;
037:
038: public static String star = "*".intern();
039:
040: static {
041: try {
042: TEXT_HTML = new MimeType("text/html");
043: APPLICATION_POSTSCRIPT = new MimeType(
044: "application/postscript");
045: TEXT_PLAIN = new MimeType("text/plain");
046: APPLICATION_X_WWW_FORM_URLENCODED = new MimeType(
047: "application/x-www-form-urlencoded");
048: APPLICATION_OCTET_STREAM = new MimeType(
049: "application/octet-stream");
050: MULTIPART_FORM_DATA = new MimeType("multipart/form-data");
051: APPLICATION_X_JAVA_AGENT = new MimeType(
052: "application/x-java-agent");
053: MESSAGE_HTTP = new MimeType("message/http");
054: TEXT_CSS = new MimeType("text/css");
055: TEXT_XML = new MimeType("text/xml");
056: TEXT = new MimeType("text/*");
057: APPLICATION_RDF_XML = new MimeType("application/rdf+xml");
058: APPLICATION_XHTML_XML = new MimeType(
059: "application/xhtml+xml");
060: APPLICATION_XML = new MimeType("application/xml");
061: } catch (MimeTypeFormatException e) {
062: System.out.println("httpd.MimeType: invalid static init.");
063: System.exit(1);
064: }
065: }
066:
067: public final static int NO_MATCH = -1;
068: public final static int MATCH_TYPE = 1;
069: public final static int MATCH_SPECIFIC_TYPE = 2;
070: public final static int MATCH_SUBTYPE = 3;
071: public final static int MATCH_SPECIFIC_SUBTYPE = 4;
072:
073: /**
074: * String representation of type
075: *
076: * @serial
077: */
078: protected String type = null;
079: /**
080: * String representation of subtype
081: *
082: * @serial
083: */
084: protected String subtype = null;
085: /**
086: * parameter names
087: *
088: * @serial
089: */
090: protected String pnames[] = null;
091: /**
092: * parameter values
093: *
094: * @serial
095: */
096: protected String pvalues[] = null;
097: /**
098: * external form of this mime type
099: *
100: * @serial
101: */
102: protected String external = null;
103:
104: /**
105: * How good the given MimeType matches the receiver of the method ?
106: * This method returns a matching level among:
107: * <dl>
108: * <dt>NO_MATCH<dd>Types not matching,</dd>
109: * <dt>MATCH_TYPE<dd>Types match,</dd>
110: * <dt>MATCH_SPECIFIC_TYPE<dd>Types match exactly,</dd>
111: * <dt>MATCH_SUBTYPE<dd>Types match, subtypes matches too</dd>
112: * <dt>MATCH_SPECIFIC_SUBTYPE<dd>Types match, subtypes matches exactly</dd>
113: * </dl>
114: * The matches are ranked from worst match to best match, a simple
115: * Max ( match[i], matched) will give the best match.
116: * @param other The other MimeType to match against ourself.
117: */
118:
119: public int match(MimeType other) {
120: int match = NO_MATCH;
121: // match types:
122: if ((type == star) || (other.type == star)) {
123: return MATCH_TYPE;
124: } else if (type != other.type) {
125: return NO_MATCH;
126: } else {
127: match = MATCH_SPECIFIC_TYPE;
128: }
129: // match subtypes:
130: if ((subtype == star) || (other.subtype == star)) {
131: match = MATCH_SUBTYPE;
132: } else if (subtype != other.subtype) {
133: return NO_MATCH;
134: } else {
135: match = MATCH_SPECIFIC_SUBTYPE;
136: }
137: return match;
138: }
139:
140: /**
141: * Find out if mime types are equivalent, based on heuristics
142: * like text/xml <=> application/xml and other problems related
143: * to format that may have multiple mime types.
144: * Note that text/html and application/xhtml+xml are not exactly
145: * the same
146: * @param mtype, a MimeType
147: * @return a boolean, true if the two mime types are equivalent
148: */
149: public boolean equiv(MimeType mtype) {
150: if (match(mtype) == MATCH_SPECIFIC_SUBTYPE) {
151: return true;
152: }
153: if ((match(TEXT_XML) == MATCH_SPECIFIC_SUBTYPE)
154: || (match(APPLICATION_XML) == MATCH_SPECIFIC_SUBTYPE)) {
155: if ((mtype.match(TEXT_XML) == MATCH_SPECIFIC_SUBTYPE)
156: || (mtype.match(APPLICATION_XML) == MATCH_SPECIFIC_SUBTYPE)) {
157: return true;
158: }
159: }
160: return false;
161: }
162:
163: /**
164: * A printable representation of this MimeType.
165: * The printed representation is guaranteed to be parseable by the
166: * String constructor.
167: */
168:
169: public String toString() {
170: if (external == null) {
171: StringBuffer sb = new StringBuffer(type);
172: sb.append((char) '/');
173: sb.append(subtype);
174: if (pnames != null) {
175: for (int i = 0; i < pnames.length; i++) {
176: sb.append(';');
177: sb.append(pnames[i]);
178: if (pvalues[i] != null) {
179: sb.append('=');
180: sb.append(pvalues[i]);
181: }
182: }
183: }
184: external = sb.toString().intern();
185: }
186: return external;
187: }
188:
189: /**
190: * Does this MIME type has some value for the given parameter ?
191: * @param name The parameter to check.
192: * @return <strong>True</strong> if this parameter has a value, false
193: * otherwise.
194: */
195: public boolean hasParameter(String name) {
196: if (name != null) {
197: if (pnames != null) {
198: String lname = name.toLowerCase();
199: for (int i = 0; i < pnames.length; i++) {
200: if (pnames[i].equals(name))
201: return true;
202: }
203: }
204: }
205: return false;
206: }
207:
208: /**
209: * Get the major type of this mime type.
210: * @return The major type, encoded as a String.
211: */
212:
213: public String getType() {
214: return type;
215: }
216:
217: /**
218: * Get the minor type (subtype) of this mime type.
219: * @return The minor or subtype encoded as a String.
220: */
221: public String getSubtype() {
222: return subtype;
223: }
224:
225: /**
226: * Get a mime type parameter value.
227: * @param name The parameter whose value is to be returned.
228: * @return The parameter value, or <b>null</b> if not found.
229: */
230: public String getParameterValue(String name) {
231: if (name != null) {
232: if (pnames != null) {
233: String lname = name.toLowerCase();
234: for (int i = 0; i < pnames.length; i++) {
235: if (pnames[i].equals(lname))
236: return pvalues[i];
237: }
238: }
239: }
240: return null;
241: }
242:
243: /**
244: * adds some parameters to a MimeType
245: * @param param a String array of parameter names
246: * @param values the corresponding String array of values
247: */
248: public void addParameters(String[] param, String[] values) {
249: // sanity check
250: if ((param == null) || (values == null)
251: || (values.length != param.length))
252: return;
253: if (pnames == null) {
254: pnames = param;
255: pvalues = values;
256: } else {
257: String[] nparam = new String[pnames.length + param.length];
258: String[] nvalues = new String[pvalues.length
259: + values.length];
260: System.arraycopy(pnames, 0, nparam, 0, pnames.length);
261: System.arraycopy(param, 0, nparam, pnames.length,
262: param.length);
263: System.arraycopy(pvalues, 0, nvalues, 0, pvalues.length);
264: System.arraycopy(values, 0, nvalues, pvalues.length,
265: values.length);
266: pnames = nparam;
267: pvalues = nvalues;
268: }
269: for (int i = 0; i < pnames.length; i++) {
270: pnames[i] = pnames[i].toLowerCase();
271: }
272: external = null;
273: }
274:
275: /**
276: * get a clone of this object
277: * @return another cloned instance of MimeType
278: */
279: public MimeType getClone() {
280: try {
281: return (MimeType) clone();
282: } catch (CloneNotSupportedException ex) {
283: // should never happen as we are Cloneable!
284: }
285: // never reached
286: return null;
287: }
288:
289: /**
290: * adds a parameterto a MimeType
291: * @param param the parameter name, as a String
292: * @param value the parameter value, as a Sting
293: */
294: public void addParameter(String param, String value) {
295: String[] p = new String[1];
296: String[] v = new String[1];
297: p[0] = param;
298: v[0] = value;
299: addParameters(p, v);
300: }
301:
302: /**
303: * Set the parameter to a MimeType (replace old value if any).
304: * @param param the parameter name, as a String
305: * @param value the parameter value, as a Sting
306: */
307: public void setParameter(String param, String value) {
308: if (pnames == null) {
309: addParameter(param, value);
310: } else {
311: String lparam = param.toLowerCase();
312: for (int i = 0; i < pnames.length; i++) {
313: if (pnames[i].equals(lparam)) {
314: pvalues[i] = value;
315: return;
316: }
317: }
318: addParameter(lparam, value);
319: }
320: }
321:
322: /**
323: * Construct MimeType object for the given string.
324: * The string should be the representation of the type. This methods
325: * tries to be compliant with HTTP1.1, p 15, although it is not
326: * (because of quoted-text not being accepted).
327: * FIXME
328: * @parameter spec A string representing a MimeType
329: * @return A MimeType object
330: * @exception MimeTypeFormatException if the string couldn't be parsed.
331: */
332: public MimeType(String spec) throws MimeTypeFormatException {
333: int strl = spec.length();
334: int start = 0, look = -1;
335: // skip leading/trailing blanks:
336: while ((start < strl) && (spec.charAt(start)) <= ' ')
337: start++;
338: while ((strl > start) && (spec.charAt(strl - 1) <= ' '))
339: strl--;
340: // get the type:
341: StringBuffer sb = new StringBuffer();
342: while ((start < strl) && ((look = spec.charAt(start)) != '/')) {
343: sb.append(Character.toLowerCase((char) look));
344: start++;
345: }
346: if (look != '/')
347: throw new MimeTypeFormatException(spec);
348: this .type = sb.toString().intern();
349: // get the subtype:
350: start++;
351: sb.setLength(0);
352: while ((start < strl) && ((look = spec.charAt(start)) > ' ')
353: && (look != ';')) {
354: sb.append(Character.toLowerCase((char) look));
355: start++;
356: }
357: this .subtype = sb.toString().intern();
358: // get parameters, if any:
359: while ((start < strl) && ((look = spec.charAt(start)) <= ' '))
360: start++;
361: if (start < strl) {
362: if (spec.charAt(start) != ';')
363: throw new MimeTypeFormatException(spec);
364: start++;
365: Vector vp = new Vector(4);
366: Vector vv = new Vector(4);
367: while (start < strl) {
368: while ((start < strl) && (spec.charAt(start) <= ' '))
369: start++;
370: // get parameter name:
371: sb.setLength(0);
372: while ((start < strl)
373: && ((look = spec.charAt(start)) > ' ')
374: && (look != '=')) {
375: sb.append(Character.toLowerCase((char) look));
376: start++;
377: }
378: String name = sb.toString();
379: // get the value:
380: while ((start < strl) && (spec.charAt(start) <= ' '))
381: start++;
382: if (spec.charAt(start) != '=')
383: throw new MimeTypeFormatException(spec);
384: start++;
385: while ((start < strl)
386: && ((spec.charAt(start) == '"') || (spec
387: .charAt(start) <= ' ')))
388: start++;
389: sb.setLength(0);
390: while ((start < strl)
391: && ((look = spec.charAt(start)) > ' ')
392: && (look != ';') && (look != '"')) {
393: sb.append((char) look);
394: start++;
395: }
396: while ((start < strl) && (spec.charAt(start) != ';'))
397: start++;
398: start++;
399: String value = sb.toString();
400: vp.addElement(name);
401: vv.addElement(value);
402: }
403: this .pnames = new String[vp.size()];
404: vp.copyInto(pnames);
405: this .pvalues = new String[vv.size()];
406: vv.copyInto(pvalues);
407: }
408: }
409:
410: public MimeType(String type, String subtype, String pnames[],
411: String pvalues[]) {
412: this .type = type.toLowerCase().intern();
413: this .subtype = subtype.toLowerCase().intern();
414: this .pnames = pnames;
415: this .pvalues = pvalues;
416: }
417:
418: public MimeType(String type, String subtype) {
419: this .type = type.toLowerCase().intern();
420: this .subtype = subtype.toLowerCase().intern();
421: }
422:
423: public static void main(String args[]) {
424: if (args.length == 1) {
425: MimeType type = null;
426: try {
427: type = new MimeType(args[0]);
428: } catch (MimeTypeFormatException e) {
429: }
430: if (type != null) {
431: System.out.println(type);
432: if (type.getClone().match(type) == MATCH_SPECIFIC_SUBTYPE) {
433: System.out.println("Clone OK");
434: } else {
435: System.out.println("Cloning failed");
436: }
437: } else {
438: System.out.println("Invalid mime type specification.");
439: }
440: } else {
441: System.out.println("Usage: java MimeType <type-to-parse>");
442: }
443: }
444:
445: }
|