aoqi@0: /* aoqi@0: * Copyright (c) 1997, 2005, Oracle and/or its affiliates. All rights reserved. aoqi@0: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. aoqi@0: * aoqi@0: * This code is free software; you can redistribute it and/or modify it aoqi@0: * under the terms of the GNU General Public License version 2 only, as aoqi@0: * published by the Free Software Foundation. Oracle designates this aoqi@0: * particular file as subject to the "Classpath" exception as provided aoqi@0: * by Oracle in the LICENSE file that accompanied this code. aoqi@0: * aoqi@0: * This code is distributed in the hope that it will be useful, but WITHOUT aoqi@0: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or aoqi@0: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License aoqi@0: * version 2 for more details (a copy is included in the LICENSE file that aoqi@0: * accompanied this code). aoqi@0: * aoqi@0: * You should have received a copy of the GNU General Public License version aoqi@0: * 2 along with this work; if not, write to the Free Software Foundation, aoqi@0: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. aoqi@0: * aoqi@0: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA aoqi@0: * or visit www.oracle.com if you need additional information or have any aoqi@0: * questions. aoqi@0: */ aoqi@0: aoqi@0: package javax.activation; aoqi@0: aoqi@0: import java.io.*; aoqi@0: import java.util.Locale; aoqi@0: aoqi@0: /** aoqi@0: * A Multipurpose Internet Mail Extension (MIME) type, as defined aoqi@0: * in RFC 2045 and 2046. aoqi@0: * aoqi@0: * @since 1.6 aoqi@0: */ aoqi@0: public class MimeType implements Externalizable { aoqi@0: aoqi@0: private String primaryType; aoqi@0: private String subType; aoqi@0: private MimeTypeParameterList parameters; aoqi@0: aoqi@0: /** aoqi@0: * A string that holds all the special chars. aoqi@0: */ aoqi@0: private static final String TSPECIALS = "()<>@,;:/[]?=\\\""; aoqi@0: aoqi@0: /** aoqi@0: * Default constructor. aoqi@0: */ aoqi@0: public MimeType() { aoqi@0: primaryType = "application"; aoqi@0: subType = "*"; aoqi@0: parameters = new MimeTypeParameterList(); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Constructor that builds a MimeType from a String. aoqi@0: * aoqi@0: * @param rawdata the MIME type string aoqi@0: */ aoqi@0: public MimeType(String rawdata) throws MimeTypeParseException { aoqi@0: parse(rawdata); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Constructor that builds a MimeType with the given primary and sub type aoqi@0: * but has an empty parameter list. aoqi@0: * aoqi@0: * @param primary the primary MIME type aoqi@0: * @param sub the MIME sub-type aoqi@0: * @exception MimeTypeParseException if the primary type or subtype aoqi@0: * is not a valid token aoqi@0: */ aoqi@0: public MimeType(String primary, String sub) throws MimeTypeParseException { aoqi@0: // check to see if primary is valid aoqi@0: if (isValidToken(primary)) { aoqi@0: primaryType = primary.toLowerCase(Locale.ENGLISH); aoqi@0: } else { aoqi@0: throw new MimeTypeParseException("Primary type is invalid."); aoqi@0: } aoqi@0: aoqi@0: // check to see if sub is valid aoqi@0: if (isValidToken(sub)) { aoqi@0: subType = sub.toLowerCase(Locale.ENGLISH); aoqi@0: } else { aoqi@0: throw new MimeTypeParseException("Sub type is invalid."); aoqi@0: } aoqi@0: aoqi@0: parameters = new MimeTypeParameterList(); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * A routine for parsing the MIME type out of a String. aoqi@0: */ aoqi@0: private void parse(String rawdata) throws MimeTypeParseException { aoqi@0: int slashIndex = rawdata.indexOf('/'); aoqi@0: int semIndex = rawdata.indexOf(';'); aoqi@0: if ((slashIndex < 0) && (semIndex < 0)) { aoqi@0: // neither character is present, so treat it aoqi@0: // as an error aoqi@0: throw new MimeTypeParseException("Unable to find a sub type."); aoqi@0: } else if ((slashIndex < 0) && (semIndex >= 0)) { aoqi@0: // we have a ';' (and therefore a parameter list), aoqi@0: // but no '/' indicating a sub type is present aoqi@0: throw new MimeTypeParseException("Unable to find a sub type."); aoqi@0: } else if ((slashIndex >= 0) && (semIndex < 0)) { aoqi@0: // we have a primary and sub type but no parameter list aoqi@0: primaryType = rawdata.substring(0, slashIndex).trim(). aoqi@0: toLowerCase(Locale.ENGLISH); aoqi@0: subType = rawdata.substring(slashIndex + 1).trim(). aoqi@0: toLowerCase(Locale.ENGLISH); aoqi@0: parameters = new MimeTypeParameterList(); aoqi@0: } else if (slashIndex < semIndex) { aoqi@0: // we have all three items in the proper sequence aoqi@0: primaryType = rawdata.substring(0, slashIndex).trim(). aoqi@0: toLowerCase(Locale.ENGLISH); aoqi@0: subType = rawdata.substring(slashIndex + 1, semIndex).trim(). aoqi@0: toLowerCase(Locale.ENGLISH); aoqi@0: parameters = new MimeTypeParameterList(rawdata.substring(semIndex)); aoqi@0: } else { aoqi@0: // we have a ';' lexically before a '/' which means we aoqi@0: // have a primary type and a parameter list but no sub type aoqi@0: throw new MimeTypeParseException("Unable to find a sub type."); aoqi@0: } aoqi@0: aoqi@0: // now validate the primary and sub types aoqi@0: aoqi@0: // check to see if primary is valid aoqi@0: if (!isValidToken(primaryType)) aoqi@0: throw new MimeTypeParseException("Primary type is invalid."); aoqi@0: aoqi@0: // check to see if sub is valid aoqi@0: if (!isValidToken(subType)) aoqi@0: throw new MimeTypeParseException("Sub type is invalid."); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Retrieve the primary type of this object. aoqi@0: * aoqi@0: * @return the primary MIME type aoqi@0: */ aoqi@0: public String getPrimaryType() { aoqi@0: return primaryType; aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Set the primary type for this object to the given String. aoqi@0: * aoqi@0: * @param primary the primary MIME type aoqi@0: * @exception MimeTypeParseException if the primary type aoqi@0: * is not a valid token aoqi@0: */ aoqi@0: public void setPrimaryType(String primary) throws MimeTypeParseException { aoqi@0: // check to see if primary is valid aoqi@0: if (!isValidToken(primaryType)) aoqi@0: throw new MimeTypeParseException("Primary type is invalid."); aoqi@0: primaryType = primary.toLowerCase(Locale.ENGLISH); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Retrieve the subtype of this object. aoqi@0: * aoqi@0: * @return the MIME subtype aoqi@0: */ aoqi@0: public String getSubType() { aoqi@0: return subType; aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Set the subtype for this object to the given String. aoqi@0: * aoqi@0: * @param sub the MIME subtype aoqi@0: * @exception MimeTypeParseException if the subtype aoqi@0: * is not a valid token aoqi@0: */ aoqi@0: public void setSubType(String sub) throws MimeTypeParseException { aoqi@0: // check to see if sub is valid aoqi@0: if (!isValidToken(subType)) aoqi@0: throw new MimeTypeParseException("Sub type is invalid."); aoqi@0: subType = sub.toLowerCase(Locale.ENGLISH); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Retrieve this object's parameter list. aoqi@0: * aoqi@0: * @return a MimeTypeParameterList object representing the parameters aoqi@0: */ aoqi@0: public MimeTypeParameterList getParameters() { aoqi@0: return parameters; aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Retrieve the value associated with the given name, or null if there aoqi@0: * is no current association. aoqi@0: * aoqi@0: * @param name the parameter name aoqi@0: * @return the paramter's value aoqi@0: */ aoqi@0: public String getParameter(String name) { aoqi@0: return parameters.get(name); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Set the value to be associated with the given name, replacing aoqi@0: * any previous association. aoqi@0: * aoqi@0: * @param name the parameter name aoqi@0: * @param value the paramter's value aoqi@0: */ aoqi@0: public void setParameter(String name, String value) { aoqi@0: parameters.set(name, value); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Remove any value associated with the given name. aoqi@0: * aoqi@0: * @param name the parameter name aoqi@0: */ aoqi@0: public void removeParameter(String name) { aoqi@0: parameters.remove(name); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Return the String representation of this object. aoqi@0: */ aoqi@0: public String toString() { aoqi@0: return getBaseType() + parameters.toString(); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Return a String representation of this object aoqi@0: * without the parameter list. aoqi@0: * aoqi@0: * @return the MIME type and sub-type aoqi@0: */ aoqi@0: public String getBaseType() { aoqi@0: return primaryType + "/" + subType; aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Determine if the primary and sub type of this object is aoqi@0: * the same as what is in the given type. aoqi@0: * aoqi@0: * @param type the MimeType object to compare with aoqi@0: * @return true if they match aoqi@0: */ aoqi@0: public boolean match(MimeType type) { aoqi@0: return primaryType.equals(type.getPrimaryType()) aoqi@0: && (subType.equals("*") aoqi@0: || type.getSubType().equals("*") aoqi@0: || (subType.equals(type.getSubType()))); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Determine if the primary and sub type of this object is aoqi@0: * the same as the content type described in rawdata. aoqi@0: * aoqi@0: * @param rawdata the MIME type string to compare with aoqi@0: * @return true if they match aoqi@0: */ aoqi@0: public boolean match(String rawdata) throws MimeTypeParseException { aoqi@0: return match(new MimeType(rawdata)); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * The object implements the writeExternal method to save its contents aoqi@0: * by calling the methods of DataOutput for its primitive values or aoqi@0: * calling the writeObject method of ObjectOutput for objects, strings aoqi@0: * and arrays. aoqi@0: * aoqi@0: * @param out the ObjectOutput object to write to aoqi@0: * @exception IOException Includes any I/O exceptions that may occur aoqi@0: */ aoqi@0: public void writeExternal(ObjectOutput out) throws IOException { aoqi@0: out.writeUTF(toString()); aoqi@0: out.flush(); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * The object implements the readExternal method to restore its aoqi@0: * contents by calling the methods of DataInput for primitive aoqi@0: * types and readObject for objects, strings and arrays. The aoqi@0: * readExternal method must read the values in the same sequence aoqi@0: * and with the same types as were written by writeExternal. aoqi@0: * aoqi@0: * @param in the ObjectInput object to read from aoqi@0: * @exception ClassNotFoundException If the class for an object being aoqi@0: * restored cannot be found. aoqi@0: */ aoqi@0: public void readExternal(ObjectInput in) aoqi@0: throws IOException, ClassNotFoundException { aoqi@0: try { aoqi@0: parse(in.readUTF()); aoqi@0: } catch (MimeTypeParseException e) { aoqi@0: throw new IOException(e.toString()); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: // below here be scary parsing related things aoqi@0: aoqi@0: /** aoqi@0: * Determine whether or not a given character belongs to a legal token. aoqi@0: */ aoqi@0: private static boolean isTokenChar(char c) { aoqi@0: return ((c > 040) && (c < 0177)) && (TSPECIALS.indexOf(c) < 0); aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Determine whether or not a given string is a legal token. aoqi@0: */ aoqi@0: private boolean isValidToken(String s) { aoqi@0: int len = s.length(); aoqi@0: if (len > 0) { aoqi@0: for (int i = 0; i < len; ++i) { aoqi@0: char c = s.charAt(i); aoqi@0: if (!isTokenChar(c)) { aoqi@0: return false; aoqi@0: } aoqi@0: } aoqi@0: return true; aoqi@0: } else { aoqi@0: return false; aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * A simple parser test, aoqi@0: * for debugging... aoqi@0: * aoqi@0: public static void main(String[] args) aoqi@0: throws MimeTypeParseException, IOException { aoqi@0: for (int i = 0; i < args.length; ++i) { aoqi@0: System.out.println("Original: " + args[i]); aoqi@0: aoqi@0: MimeType type = new MimeType(args[i]); aoqi@0: aoqi@0: System.out.println("Short: " + type.getBaseType()); aoqi@0: System.out.println("Parsed: " + type.toString()); aoqi@0: System.out.println(); aoqi@0: } aoqi@0: } aoqi@0: */ aoqi@0: }