1. /*
  2. * @(#)MimeType.java 1.24 03/01/23
  3. *
  4. * Copyright 2003 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. import java.util.Enumeration;
  13. /**
  14. * A Multipurpose Internet Mail Extension (MIME) type, as defined
  15. * in RFC 2045 and 2046.
  16. *
  17. * THIS IS *NOT* - REPEAT *NOT* - A PUBLIC CLASS! DataFlavor IS
  18. * THE PUBLIC INTERFACE, AND THIS IS PROVIDED AS A ***PRIVATE***
  19. * (THAT IS AS IN *NOT* PUBLIC) HELPER CLASS!
  20. */
  21. class MimeType implements Externalizable, Cloneable {
  22. /*
  23. * serialization support
  24. */
  25. static final long serialVersionUID = -6568722458793895906L;
  26. /**
  27. * Constructor for externalization; this constructor should not be
  28. * called directly by an application, since the result will be an
  29. * uninitialized, immutable <code>MimeType</code> object.
  30. */
  31. public MimeType() {
  32. }
  33. /**
  34. * Builds a <code>MimeType</code> from a <code>String</code>.
  35. *
  36. * @param rawdata text used to initialize the <code>MimeType</code>
  37. * @throws NullPointerException if <code>rawdata</code> is null
  38. */
  39. public MimeType(String rawdata) throws MimeTypeParseException {
  40. parse(rawdata);
  41. }
  42. /**
  43. * Builds a <code>MimeType</code> with the given primary and sub
  44. * type but has an empty parameter list.
  45. *
  46. * @param primary the primary type of this <code>MimeType</code>
  47. * @param sub the subtype of this <code>MimeType</code>
  48. * @throws NullPointerException if either <code>primary</code> or
  49. * <code>sub</code> is null
  50. */
  51. public MimeType(String primary, String sub) throws MimeTypeParseException {
  52. this(primary, sub, new MimeTypeParameterList());
  53. }
  54. /**
  55. * Builds a <code>MimeType</code> with a pre-defined
  56. * and valid (or empty) parameter list.
  57. *
  58. * @param primary the primary type of this <code>MimeType</code>
  59. * @param sub the subtype of this <code>MimeType</code>
  60. * @param mtpl the requested parameter list
  61. * @throws NullPointerException if either <code>primary</code>,
  62. * <code>sub</code> or <code>mtpl</code> is null
  63. */
  64. public MimeType(String primary, String sub, MimeTypeParameterList mtpl) throws
  65. MimeTypeParseException {
  66. // check to see if primary is valid
  67. if(isValidToken(primary)) {
  68. primaryType = primary.toLowerCase();
  69. } else {
  70. throw new MimeTypeParseException("Primary type is invalid.");
  71. }
  72. // check to see if sub is valid
  73. if(isValidToken(sub)) {
  74. subType = sub.toLowerCase();
  75. } else {
  76. throw new MimeTypeParseException("Sub type is invalid.");
  77. }
  78. parameters = (MimeTypeParameterList)mtpl.clone();
  79. }
  80. public int hashCode() {
  81. // We sum up the hash codes for all of the strings. This
  82. // way, the order of the strings is irrelevant
  83. int code = 0;
  84. code += primaryType.hashCode();
  85. code += subType.hashCode();
  86. code += parameters.hashCode();
  87. return code;
  88. } // hashCode()
  89. /**
  90. * <code>MimeType</code>s are equal if their primary types,
  91. * subtypes, and parameters are all equal. No default values
  92. * are taken into account.
  93. * @param thatObject the object to be evaluated as a
  94. * <code>MimeType</code>
  95. * @return <code>true</code> if <code>thatObject</code> is
  96. * a <code>MimeType</code> otherwise returns <code>false</code>
  97. */
  98. public boolean equals(Object thatObject) {
  99. if (!(thatObject instanceof MimeType)) {
  100. return false;
  101. }
  102. MimeType that = (MimeType)thatObject;
  103. boolean isIt =
  104. ((this.primaryType.equals(that.primaryType)) &&
  105. (this.subType.equals(that.subType)) &&
  106. (this.parameters.equals(that.parameters)));
  107. return isIt;
  108. } // equals()
  109. /**
  110. * A routine for parsing the MIME type out of a String.
  111. *
  112. * @throws NullPointerException if <code>rawdata</code> is null
  113. */
  114. private void parse(String rawdata) throws MimeTypeParseException {
  115. int slashIndex = rawdata.indexOf('/');
  116. int semIndex = rawdata.indexOf(';');
  117. if((slashIndex < 0) && (semIndex < 0)) {
  118. // neither character is present, so treat it
  119. // as an error
  120. throw new MimeTypeParseException("Unable to find a sub type.");
  121. } else if((slashIndex < 0) && (semIndex >= 0)) {
  122. // we have a ';' (and therefore a parameter list),
  123. // but no '/' indicating a sub type is present
  124. throw new MimeTypeParseException("Unable to find a sub type.");
  125. } else if((slashIndex >= 0) && (semIndex < 0)) {
  126. // we have a primary and sub type but no parameter list
  127. primaryType = rawdata.substring(0,
  128. slashIndex).trim().toLowerCase();
  129. subType = rawdata.substring(slashIndex +
  130. 1).trim().toLowerCase();
  131. parameters = new MimeTypeParameterList();
  132. } else if (slashIndex < semIndex) {
  133. // we have all three items in the proper sequence
  134. primaryType = rawdata.substring(0,
  135. slashIndex).trim().toLowerCase();
  136. subType = rawdata.substring(slashIndex + 1,
  137. semIndex).trim().toLowerCase();
  138. parameters = new
  139. MimeTypeParameterList(rawdata.substring(semIndex));
  140. } else {
  141. // we have a ';' lexically before a '/' which means we have a primary type
  142. // & a parameter list but no sub type
  143. throw new MimeTypeParseException("Unable to find a sub type.");
  144. }
  145. // now validate the primary and sub types
  146. // check to see if primary is valid
  147. if(!isValidToken(primaryType)) {
  148. throw new MimeTypeParseException("Primary type is invalid.");
  149. }
  150. // check to see if sub is valid
  151. if(!isValidToken(subType)) {
  152. throw new MimeTypeParseException("Sub type is invalid.");
  153. }
  154. }
  155. /**
  156. * Retrieve the primary type of this object.
  157. */
  158. public String getPrimaryType() {
  159. return primaryType;
  160. }
  161. /**
  162. * Retrieve the sub type of this object.
  163. */
  164. public String getSubType() {
  165. return subType;
  166. }
  167. /**
  168. * Retrieve a copy of this object's parameter list.
  169. */
  170. public MimeTypeParameterList getParameters() {
  171. return (MimeTypeParameterList)parameters.clone();
  172. }
  173. /**
  174. * Retrieve the value associated with the given name, or null if there
  175. * is no current association.
  176. */
  177. public String getParameter(String name) {
  178. return parameters.get(name);
  179. }
  180. /**
  181. * Set the value to be associated with the given name, replacing
  182. * any previous association.
  183. *
  184. * @throw IllegalArgumentException if parameter or value is illegal
  185. */
  186. public void setParameter(String name, String value) {
  187. parameters.set(name, value);
  188. }
  189. /**
  190. * Remove any value associated with the given name.
  191. *
  192. * @throw IllegalArgumentExcpetion if parameter may not be deleted
  193. */
  194. public void removeParameter(String name) {
  195. parameters.remove(name);
  196. }
  197. /**
  198. * Return the String representation of this object.
  199. */
  200. public String toString() {
  201. return getBaseType() + parameters.toString();
  202. }
  203. /**
  204. * Return a String representation of this object
  205. * without the parameter list.
  206. */
  207. public String getBaseType() {
  208. return primaryType + "/" + subType;
  209. }
  210. /**
  211. * Returns <code>true</code> if the primary type and the
  212. * subtype of this object are the same as the specified
  213. * <code>type</code> otherwise returns <code>false</code>.
  214. *
  215. * @param type the type to compare to <code>this</code>'s type
  216. * @return <code>true</code> if the primary type and the
  217. * subtype of this object are the same as the
  218. * specified <code>type</code> otherwise returns
  219. * <code>false</code>
  220. */
  221. public boolean match(MimeType type) {
  222. if (type == null)
  223. return false;
  224. return primaryType.equals(type.getPrimaryType())
  225. && (subType.equals("*")
  226. || type.getSubType().equals("*")
  227. || (subType.equals(type.getSubType())));
  228. }
  229. /**
  230. * Returns <code>true</code> if the primary type and the
  231. * subtype of this object are the same as the content type
  232. * described in <code>rawdata</code> otherwise returns
  233. * <code>false</code>.
  234. *
  235. * @param rawdata the raw data to be examined
  236. * @return <code>true</code> if the primary type and the
  237. * subtype of this object are the same as the content type
  238. * described in <code>rawdata</code> otherwise returns
  239. * <code>false</code> if <code>rawdata</code> is
  240. * <code>null</code>, returns <code>false</code>
  241. */
  242. public boolean match(String rawdata) throws MimeTypeParseException {
  243. if (rawdata == null)
  244. return false;
  245. return match(new MimeType(rawdata));
  246. }
  247. /**
  248. * The object implements the writeExternal method to save its contents
  249. * by calling the methods of DataOutput for its primitive values or
  250. * calling the writeObject method of ObjectOutput for objects, strings
  251. * and arrays.
  252. * @exception IOException Includes any I/O exceptions that may occur
  253. */
  254. public void writeExternal(ObjectOutput out) throws IOException {
  255. String s = toString(); // contains ASCII chars only
  256. // one-to-one correspondence between ASCII char and byte in UTF string
  257. if (s.length() <= 65535) { // 65535 is max length of UTF string
  258. out.writeUTF(s);
  259. } else {
  260. out.writeByte(0);
  261. out.writeByte(0);
  262. out.writeInt(s.length());
  263. out.write(s.getBytes());
  264. }
  265. }
  266. /**
  267. * The object implements the readExternal method to restore its
  268. * contents by calling the methods of DataInput for primitive
  269. * types and readObject for objects, strings and arrays. The
  270. * readExternal method must read the values in the same sequence
  271. * and with the same types as were written by writeExternal.
  272. * @exception ClassNotFoundException If the class for an object being
  273. * restored cannot be found.
  274. */
  275. public void readExternal(ObjectInput in) throws IOException,
  276. ClassNotFoundException {
  277. String s = in.readUTF();
  278. if (s == null || s.length() == 0) { // long mime type
  279. byte[] ba = new byte[in.readInt()];
  280. in.readFully(ba);
  281. s = new String(ba);
  282. }
  283. try {
  284. parse(s);
  285. } catch(MimeTypeParseException e) {
  286. throw new IOException(e.toString());
  287. }
  288. }
  289. /**
  290. * Returns a clone of this object.
  291. * @return a clone of this object
  292. */
  293. public Object clone() {
  294. MimeType newObj = null;
  295. try {
  296. newObj = (MimeType)super.clone();
  297. } catch (CloneNotSupportedException cannotHappen) {
  298. }
  299. newObj.parameters = (MimeTypeParameterList)parameters.clone();
  300. return newObj;
  301. }
  302. private String primaryType;
  303. private String subType;
  304. private MimeTypeParameterList parameters;
  305. // below here be scary parsing related things
  306. /**
  307. * Determines whether or not a given character belongs to a legal token.
  308. */
  309. private static boolean isTokenChar(char c) {
  310. return ((c > 040) && (c < 0177)) && (TSPECIALS.indexOf(c) < 0);
  311. }
  312. /**
  313. * Determines whether or not a given string is a legal token.
  314. *
  315. * @throws NullPointerException if <code>s</code> is null
  316. */
  317. private boolean isValidToken(String s) {
  318. int len = s.length();
  319. if(len > 0) {
  320. for (int i = 0; i < len; ++i) {
  321. char c = s.charAt(i);
  322. if (!isTokenChar(c)) {
  323. return false;
  324. }
  325. }
  326. return true;
  327. } else {
  328. return false;
  329. }
  330. }
  331. /**
  332. * A string that holds all the special chars.
  333. */
  334. private static final String TSPECIALS = "()<>@,;:\\\"/[]?=";
  335. } // class MimeType