1. /*
  2. * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
  3. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  4. */
  5. package javax.activation;
  6. import java.io.ObjectOutput;
  7. import java.io.ObjectInput;
  8. import java.io.IOException;
  9. import java.io.*;
  10. /**
  11. * A Multipurpose Internet Mail Extension (MIME) type, as defined
  12. * in RFC 2045 and 2046.
  13. */
  14. public class MimeType implements Externalizable {
  15. private String primaryType;
  16. private String subType;
  17. private MimeTypeParameterList parameters;
  18. /**
  19. * A string that holds all the special chars.
  20. */
  21. private static final String TSPECIALS = "()<>@,;:/[]?=\\\"";
  22. /**
  23. * Default constructor.
  24. */
  25. public MimeType() {
  26. primaryType = "application";
  27. subType = "*";
  28. parameters = new MimeTypeParameterList();
  29. }
  30. /**
  31. * Constructor that builds a MimeType from a String.
  32. *
  33. * @param rawdata the MIME type string
  34. */
  35. public MimeType(String rawdata) throws MimeTypeParseException {
  36. parse(rawdata);
  37. }
  38. /**
  39. * Constructor that builds a MimeType with the given primary and sub type
  40. * but has an empty parameter list.
  41. *
  42. * @param primary the primary MIME type
  43. * @param sub the MIME sub-type
  44. */
  45. public MimeType(String primary, String sub) throws MimeTypeParseException {
  46. // check to see if primary is valid
  47. if (isValidToken(primary)) {
  48. primaryType = primary.toLowerCase();
  49. } else {
  50. throw new MimeTypeParseException("Primary type is invalid.");
  51. }
  52. // check to see if sub is valid
  53. if (isValidToken(sub)) {
  54. subType = sub.toLowerCase();
  55. } else {
  56. throw new MimeTypeParseException("Sub type is invalid.");
  57. }
  58. parameters = new MimeTypeParameterList();
  59. }
  60. /**
  61. * A routine for parsing the MIME type out of a String.
  62. */
  63. private void parse(String rawdata) throws MimeTypeParseException {
  64. int slashIndex = rawdata.indexOf('/');
  65. int semIndex = rawdata.indexOf(';');
  66. if ((slashIndex < 0) && (semIndex < 0)) {
  67. // neither character is present, so treat it
  68. // as an error
  69. throw new MimeTypeParseException("Unable to find a sub type.");
  70. } else if ((slashIndex < 0) && (semIndex >= 0)) {
  71. // we have a ';' (and therefore a parameter list),
  72. // but no '/' indicating a sub type is present
  73. throw new MimeTypeParseException("Unable to find a sub type.");
  74. } else if ((slashIndex >= 0) && (semIndex < 0)) {
  75. // we have a primary and sub type but no parameter list
  76. primaryType = rawdata.substring(0, slashIndex).trim().toLowerCase();
  77. subType = rawdata.substring(slashIndex + 1).trim().toLowerCase();
  78. parameters = new MimeTypeParameterList();
  79. } else if (slashIndex < semIndex) {
  80. // we have all three items in the proper sequence
  81. primaryType = rawdata.substring(0, slashIndex).trim().toLowerCase();
  82. subType = rawdata.substring(slashIndex + 1,
  83. semIndex).trim().toLowerCase();
  84. parameters = new MimeTypeParameterList(rawdata.substring(semIndex));
  85. } else {
  86. // we have a ';' lexically before a '/' which means we
  87. // have a primary type and a parameter list but no sub type
  88. throw new MimeTypeParseException("Unable to find a sub type.");
  89. }
  90. // now validate the primary and sub types
  91. // check to see if primary is valid
  92. if (!isValidToken(primaryType))
  93. throw new MimeTypeParseException("Primary type is invalid.");
  94. // check to see if sub is valid
  95. if (!isValidToken(subType))
  96. throw new MimeTypeParseException("Sub type is invalid.");
  97. }
  98. /**
  99. * Retrieve the primary type of this object.
  100. *
  101. * @return the primary MIME type
  102. */
  103. public String getPrimaryType() {
  104. return primaryType;
  105. }
  106. /**
  107. * Set the primary type for this object to the given String.
  108. *
  109. * @param primary the primary MIME type
  110. */
  111. public void setPrimaryType(String primary) throws MimeTypeParseException {
  112. // check to see if primary is valid
  113. if (!isValidToken(primaryType))
  114. throw new MimeTypeParseException("Primary type is invalid.");
  115. primaryType = primary.toLowerCase();
  116. }
  117. /**
  118. * Retrieve the sub type of this object.
  119. *
  120. * @return the MIME sub-type
  121. */
  122. public String getSubType() {
  123. return subType;
  124. }
  125. /**
  126. * Set the sub type for this object to the given String.
  127. *
  128. * @param sub the MIME sub-type
  129. */
  130. public void setSubType(String sub) throws MimeTypeParseException {
  131. // check to see if sub is valid
  132. if (!isValidToken(subType))
  133. throw new MimeTypeParseException("Sub type is invalid.");
  134. subType = sub.toLowerCase();
  135. }
  136. /**
  137. * Retrieve this object's parameter list.
  138. *
  139. * @return a MimeTypeParameterList object representing the parameters
  140. */
  141. public MimeTypeParameterList getParameters() {
  142. return parameters;
  143. }
  144. /**
  145. * Retrieve the value associated with the given name, or null if there
  146. * is no current association.
  147. *
  148. * @param name the parameter name
  149. * @return the paramter's value
  150. */
  151. public String getParameter(String name) {
  152. return parameters.get(name);
  153. }
  154. /**
  155. * Set the value to be associated with the given name, replacing
  156. * any previous association.
  157. *
  158. * @param name the parameter name
  159. * @param value the paramter's value
  160. */
  161. public void setParameter(String name, String value) {
  162. parameters.set(name, value);
  163. }
  164. /**
  165. * Remove any value associated with the given name.
  166. *
  167. * @param name the parameter name
  168. */
  169. public void removeParameter(String name) {
  170. parameters.remove(name);
  171. }
  172. /**
  173. * Return the String representation of this object.
  174. */
  175. public String toString() {
  176. return getBaseType() + parameters.toString();
  177. }
  178. /**
  179. * Return a String representation of this object
  180. * without the parameter list.
  181. *
  182. * @return the MIME type and sub-type
  183. */
  184. public String getBaseType() {
  185. return primaryType + "/" + subType;
  186. }
  187. /**
  188. * Determine if the primary and sub type of this object is
  189. * the same as what is in the given type.
  190. *
  191. * @param type the MimeType object to compare with
  192. * @return true if they match
  193. */
  194. public boolean match(MimeType type) {
  195. return primaryType.equals(type.getPrimaryType())
  196. && (subType.equals("*")
  197. || type.getSubType().equals("*")
  198. || (subType.equals(type.getSubType())));
  199. }
  200. /**
  201. * Determine if the primary and sub type of this object is
  202. * the same as the content type described in rawdata.
  203. *
  204. * @param rawdata the MIME type string to compare with
  205. * @return true if they match
  206. */
  207. public boolean match(String rawdata) throws MimeTypeParseException {
  208. return match(new MimeType(rawdata));
  209. }
  210. /**
  211. * The object implements the writeExternal method to save its contents
  212. * by calling the methods of DataOutput for its primitive values or
  213. * calling the writeObject method of ObjectOutput for objects, strings
  214. * and arrays.
  215. *
  216. * @param out the ObjectOutput object to write to
  217. * @exception IOException Includes any I/O exceptions that may occur
  218. */
  219. public void writeExternal(ObjectOutput out) throws IOException {
  220. out.writeUTF(toString());
  221. out.flush();
  222. }
  223. /**
  224. * The object implements the readExternal method to restore its
  225. * contents by calling the methods of DataInput for primitive
  226. * types and readObject for objects, strings and arrays. The
  227. * readExternal method must read the values in the same sequence
  228. * and with the same types as were written by writeExternal.
  229. *
  230. * @param in the IbjectInput object to read from
  231. * @exception ClassNotFoundException If the class for an object being
  232. * restored cannot be found.
  233. */
  234. public void readExternal(ObjectInput in)
  235. throws IOException, ClassNotFoundException {
  236. try {
  237. parse(in.readUTF());
  238. } catch (MimeTypeParseException e) {
  239. throw new IOException(e.toString());
  240. }
  241. }
  242. // below here be scary parsing related things
  243. /**
  244. * Determine whether or not a given character belongs to a legal token.
  245. */
  246. private static boolean isTokenChar(char c) {
  247. return ((c > 040) && (c < 0177)) && (TSPECIALS.indexOf(c) < 0);
  248. }
  249. /**
  250. * Determine whether or not a given string is a legal token.
  251. */
  252. private boolean isValidToken(String s) {
  253. int len = s.length();
  254. if (len > 0) {
  255. for (int i = 0; i < len; ++i) {
  256. char c = s.charAt(i);
  257. if (!isTokenChar(c)) {
  258. return false;
  259. }
  260. }
  261. return true;
  262. } else {
  263. return false;
  264. }
  265. }
  266. /**
  267. * A simple parser test,
  268. * for debugging...
  269. *
  270. public static void main(String[] args)
  271. throws MimeTypeParseException, IOException {
  272. for (int i = 0; i < args.length; ++i) {
  273. System.out.println("Original: " + args[i]);
  274. MimeType type = new MimeType(args[i]);
  275. System.out.println("Short: " + type.getBaseType());
  276. System.out.println("Parsed: " + type.toString());
  277. System.out.println();
  278. }
  279. }
  280. */
  281. }