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