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