jdk/src/java.desktop/share/classes/java/awt/datatransfer/MimeType.java
changeset 29046 c46ad26fb278
parent 29045 991394bd2448
parent 29044 37c6512bd1df
child 29048 39a3bcf89286
equal deleted inserted replaced
29045:991394bd2448 29046:c46ad26fb278
     1 /*
       
     2  * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.  Oracle designates this
       
     8  * particular file as subject to the "Classpath" exception as provided
       
     9  * by Oracle in the LICENSE file that accompanied this code.
       
    10  *
       
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    14  * version 2 for more details (a copy is included in the LICENSE file that
       
    15  * accompanied this code).
       
    16  *
       
    17  * You should have received a copy of the GNU General Public License version
       
    18  * 2 along with this work; if not, write to the Free Software Foundation,
       
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    20  *
       
    21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
       
    22  * or visit www.oracle.com if you need additional information or have any
       
    23  * questions.
       
    24  */
       
    25 
       
    26 package java.awt.datatransfer;
       
    27 
       
    28 import java.io.Externalizable;
       
    29 import java.io.ObjectOutput;
       
    30 import java.io.ObjectInput;
       
    31 import java.io.IOException;
       
    32 import java.util.Enumeration;
       
    33 import java.util.Locale;
       
    34 
       
    35 
       
    36 /**
       
    37  * A Multipurpose Internet Mail Extension (MIME) type, as defined
       
    38  * in RFC 2045 and 2046.
       
    39  *
       
    40  * THIS IS *NOT* - REPEAT *NOT* - A PUBLIC CLASS! DataFlavor IS
       
    41  * THE PUBLIC INTERFACE, AND THIS IS PROVIDED AS A ***PRIVATE***
       
    42  * (THAT IS AS IN *NOT* PUBLIC) HELPER CLASS!
       
    43  */
       
    44 class MimeType implements Externalizable, Cloneable {
       
    45 
       
    46     /*
       
    47      * serialization support
       
    48      */
       
    49 
       
    50     static final long serialVersionUID = -6568722458793895906L;
       
    51 
       
    52     /**
       
    53      * Constructor for externalization; this constructor should not be
       
    54      * called directly by an application, since the result will be an
       
    55      * uninitialized, immutable <code>MimeType</code> object.
       
    56      */
       
    57     public MimeType() {
       
    58     }
       
    59 
       
    60     /**
       
    61      * Builds a <code>MimeType</code> from a <code>String</code>.
       
    62      *
       
    63      * @param rawdata text used to initialize the <code>MimeType</code>
       
    64      * @throws NullPointerException if <code>rawdata</code> is null
       
    65      */
       
    66     public MimeType(String rawdata) throws MimeTypeParseException {
       
    67         parse(rawdata);
       
    68     }
       
    69 
       
    70     /**
       
    71      * Builds a <code>MimeType</code> with the given primary and sub
       
    72      * type but has an empty parameter list.
       
    73      *
       
    74      * @param primary the primary type of this <code>MimeType</code>
       
    75      * @param sub the subtype of this <code>MimeType</code>
       
    76      * @throws NullPointerException if either <code>primary</code> or
       
    77      *         <code>sub</code> is null
       
    78      */
       
    79     public MimeType(String primary, String sub) throws MimeTypeParseException {
       
    80         this(primary, sub, new MimeTypeParameterList());
       
    81     }
       
    82 
       
    83     /**
       
    84      * Builds a <code>MimeType</code> with a pre-defined
       
    85      * and valid (or empty) parameter list.
       
    86      *
       
    87      * @param primary the primary type of this <code>MimeType</code>
       
    88      * @param sub the subtype of this <code>MimeType</code>
       
    89      * @param mtpl the requested parameter list
       
    90      * @throws NullPointerException if either <code>primary</code>,
       
    91      *         <code>sub</code> or <code>mtpl</code> is null
       
    92      */
       
    93     public MimeType(String primary, String sub, MimeTypeParameterList mtpl) throws
       
    94 MimeTypeParseException {
       
    95         //    check to see if primary is valid
       
    96         if(isValidToken(primary)) {
       
    97             primaryType = primary.toLowerCase(Locale.ENGLISH);
       
    98         } else {
       
    99             throw new MimeTypeParseException("Primary type is invalid.");
       
   100         }
       
   101 
       
   102         //    check to see if sub is valid
       
   103         if(isValidToken(sub)) {
       
   104             subType = sub.toLowerCase(Locale.ENGLISH);
       
   105         } else {
       
   106             throw new MimeTypeParseException("Sub type is invalid.");
       
   107         }
       
   108 
       
   109         parameters = (MimeTypeParameterList)mtpl.clone();
       
   110     }
       
   111 
       
   112     public int hashCode() {
       
   113 
       
   114         // We sum up the hash codes for all of the strings. This
       
   115         // way, the order of the strings is irrelevant
       
   116         int code = 0;
       
   117         code += primaryType.hashCode();
       
   118         code += subType.hashCode();
       
   119         code += parameters.hashCode();
       
   120         return code;
       
   121     } // hashCode()
       
   122 
       
   123     /**
       
   124      * <code>MimeType</code>s are equal if their primary types,
       
   125      * subtypes, and  parameters are all equal. No default values
       
   126      * are taken into account.
       
   127      * @param thatObject the object to be evaluated as a
       
   128      *    <code>MimeType</code>
       
   129      * @return <code>true</code> if <code>thatObject</code> is
       
   130      *    a <code>MimeType</code>; otherwise returns <code>false</code>
       
   131      */
       
   132     public boolean equals(Object thatObject) {
       
   133         if (!(thatObject instanceof MimeType)) {
       
   134             return false;
       
   135         }
       
   136         MimeType that = (MimeType)thatObject;
       
   137         boolean isIt =
       
   138             ((this.primaryType.equals(that.primaryType)) &&
       
   139              (this.subType.equals(that.subType)) &&
       
   140              (this.parameters.equals(that.parameters)));
       
   141         return isIt;
       
   142     } // equals()
       
   143 
       
   144     /**
       
   145      * A routine for parsing the MIME type out of a String.
       
   146      *
       
   147      * @throws NullPointerException if <code>rawdata</code> is null
       
   148      */
       
   149     private void parse(String rawdata) throws MimeTypeParseException {
       
   150         int slashIndex = rawdata.indexOf('/');
       
   151         int semIndex = rawdata.indexOf(';');
       
   152         if((slashIndex < 0) && (semIndex < 0)) {
       
   153             //    neither character is present, so treat it
       
   154             //    as an error
       
   155             throw new MimeTypeParseException("Unable to find a sub type.");
       
   156         } else if((slashIndex < 0) && (semIndex >= 0)) {
       
   157             //    we have a ';' (and therefore a parameter list),
       
   158             //    but no '/' indicating a sub type is present
       
   159             throw new MimeTypeParseException("Unable to find a sub type.");
       
   160         } else if((slashIndex >= 0) && (semIndex < 0)) {
       
   161             //    we have a primary and sub type but no parameter list
       
   162             primaryType = rawdata.substring(0,slashIndex).
       
   163                 trim().toLowerCase(Locale.ENGLISH);
       
   164             subType = rawdata.substring(slashIndex + 1).
       
   165                 trim().toLowerCase(Locale.ENGLISH);
       
   166             parameters = new MimeTypeParameterList();
       
   167         } else if (slashIndex < semIndex) {
       
   168             //    we have all three items in the proper sequence
       
   169             primaryType = rawdata.substring(0, slashIndex).
       
   170                 trim().toLowerCase(Locale.ENGLISH);
       
   171             subType = rawdata.substring(slashIndex + 1,
       
   172                 semIndex).trim().toLowerCase(Locale.ENGLISH);
       
   173             parameters = new
       
   174 MimeTypeParameterList(rawdata.substring(semIndex));
       
   175         } else {
       
   176             //    we have a ';' lexically before a '/' which means we have a primary type
       
   177             //    & a parameter list but no sub type
       
   178             throw new MimeTypeParseException("Unable to find a sub type.");
       
   179         }
       
   180 
       
   181         //    now validate the primary and sub types
       
   182 
       
   183         //    check to see if primary is valid
       
   184         if(!isValidToken(primaryType)) {
       
   185             throw new MimeTypeParseException("Primary type is invalid.");
       
   186         }
       
   187 
       
   188         //    check to see if sub is valid
       
   189         if(!isValidToken(subType)) {
       
   190             throw new MimeTypeParseException("Sub type is invalid.");
       
   191         }
       
   192     }
       
   193 
       
   194     /**
       
   195      * Retrieve the primary type of this object.
       
   196      */
       
   197     public String getPrimaryType() {
       
   198         return primaryType;
       
   199     }
       
   200 
       
   201     /**
       
   202      * Retrieve the sub type of this object.
       
   203      */
       
   204     public String getSubType() {
       
   205         return subType;
       
   206     }
       
   207 
       
   208     /**
       
   209      * Retrieve a copy of this object's parameter list.
       
   210      */
       
   211     public MimeTypeParameterList getParameters() {
       
   212         return (MimeTypeParameterList)parameters.clone();
       
   213     }
       
   214 
       
   215     /**
       
   216      * Retrieve the value associated with the given name, or null if there
       
   217      * is no current association.
       
   218      */
       
   219     public String getParameter(String name) {
       
   220         return parameters.get(name);
       
   221     }
       
   222 
       
   223     /**
       
   224      * Set the value to be associated with the given name, replacing
       
   225      * any previous association.
       
   226      *
       
   227      * @throw IllegalArgumentException if parameter or value is illegal
       
   228      */
       
   229     public void setParameter(String name, String value) {
       
   230         parameters.set(name, value);
       
   231     }
       
   232 
       
   233     /**
       
   234      * Remove any value associated with the given name.
       
   235      *
       
   236      * @throw IllegalArgumentException if parameter may not be deleted
       
   237      */
       
   238     public void removeParameter(String name) {
       
   239         parameters.remove(name);
       
   240     }
       
   241 
       
   242     /**
       
   243      * Return the String representation of this object.
       
   244      */
       
   245     public String toString() {
       
   246         return getBaseType() + parameters.toString();
       
   247     }
       
   248 
       
   249     /**
       
   250      * Return a String representation of this object
       
   251      * without the parameter list.
       
   252      */
       
   253     public String getBaseType() {
       
   254         return primaryType + "/" + subType;
       
   255     }
       
   256 
       
   257     /**
       
   258      * Returns <code>true</code> if the primary type and the
       
   259      * subtype of this object are the same as the specified
       
   260      * <code>type</code>; otherwise returns <code>false</code>.
       
   261      *
       
   262      * @param type the type to compare to <code>this</code>'s type
       
   263      * @return <code>true</code> if the primary type and the
       
   264      *    subtype of this object are the same as the
       
   265      *    specified <code>type</code>; otherwise returns
       
   266      *    <code>false</code>
       
   267      */
       
   268     public boolean match(MimeType type) {
       
   269         if (type == null)
       
   270             return false;
       
   271         return primaryType.equals(type.getPrimaryType())
       
   272                     && (subType.equals("*")
       
   273                             || type.getSubType().equals("*")
       
   274                             || (subType.equals(type.getSubType())));
       
   275     }
       
   276 
       
   277     /**
       
   278      * Returns <code>true</code> if the primary type and the
       
   279      * subtype of this object are the same as the content type
       
   280      * described in <code>rawdata</code>; otherwise returns
       
   281      * <code>false</code>.
       
   282      *
       
   283      * @param rawdata the raw data to be examined
       
   284      * @return <code>true</code> if the primary type and the
       
   285      *    subtype of this object are the same as the content type
       
   286      *    described in <code>rawdata</code>; otherwise returns
       
   287      *    <code>false</code>; if <code>rawdata</code> is
       
   288      *    <code>null</code>, returns <code>false</code>
       
   289      */
       
   290     public boolean match(String rawdata) throws MimeTypeParseException {
       
   291         if (rawdata == null)
       
   292             return false;
       
   293         return match(new MimeType(rawdata));
       
   294     }
       
   295 
       
   296     /**
       
   297      * The object implements the writeExternal method to save its contents
       
   298      * by calling the methods of DataOutput for its primitive values or
       
   299      * calling the writeObject method of ObjectOutput for objects, strings
       
   300      * and arrays.
       
   301      * @exception IOException Includes any I/O exceptions that may occur
       
   302      */
       
   303     public void writeExternal(ObjectOutput out) throws IOException {
       
   304         String s = toString(); // contains ASCII chars only
       
   305         // one-to-one correspondence between ASCII char and byte in UTF string
       
   306         if (s.length() <= 65535) { // 65535 is max length of UTF string
       
   307             out.writeUTF(s);
       
   308         } else {
       
   309             out.writeByte(0);
       
   310             out.writeByte(0);
       
   311             out.writeInt(s.length());
       
   312             out.write(s.getBytes());
       
   313         }
       
   314     }
       
   315 
       
   316     /**
       
   317      * The object implements the readExternal method to restore its
       
   318      * contents by calling the methods of DataInput for primitive
       
   319      * types and readObject for objects, strings and arrays.  The
       
   320      * readExternal method must read the values in the same sequence
       
   321      * and with the same types as were written by writeExternal.
       
   322      * @exception ClassNotFoundException If the class for an object being
       
   323      *              restored cannot be found.
       
   324      */
       
   325     public void readExternal(ObjectInput in) throws IOException,
       
   326 ClassNotFoundException {
       
   327         String s = in.readUTF();
       
   328         if (s == null || s.length() == 0) { // long mime type
       
   329             byte[] ba = new byte[in.readInt()];
       
   330             in.readFully(ba);
       
   331             s = new String(ba);
       
   332         }
       
   333         try {
       
   334             parse(s);
       
   335         } catch(MimeTypeParseException e) {
       
   336             throw new IOException(e.toString());
       
   337         }
       
   338     }
       
   339 
       
   340     /**
       
   341      * Returns a clone of this object.
       
   342      * @return a clone of this object
       
   343      */
       
   344 
       
   345     public Object clone() {
       
   346         MimeType newObj = null;
       
   347         try {
       
   348             newObj = (MimeType)super.clone();
       
   349         } catch (CloneNotSupportedException cannotHappen) {
       
   350         }
       
   351         newObj.parameters = (MimeTypeParameterList)parameters.clone();
       
   352         return newObj;
       
   353     }
       
   354 
       
   355     private String    primaryType;
       
   356     private String    subType;
       
   357     private MimeTypeParameterList parameters;
       
   358 
       
   359     //    below here be scary parsing related things
       
   360 
       
   361     /**
       
   362      * Determines whether or not a given character belongs to a legal token.
       
   363      */
       
   364     private static boolean isTokenChar(char c) {
       
   365         return ((c > 040) && (c < 0177)) && (TSPECIALS.indexOf(c) < 0);
       
   366     }
       
   367 
       
   368     /**
       
   369      * Determines whether or not a given string is a legal token.
       
   370      *
       
   371      * @throws NullPointerException if <code>s</code> is null
       
   372      */
       
   373     private boolean isValidToken(String s) {
       
   374         int len = s.length();
       
   375         if(len > 0) {
       
   376             for (int i = 0; i < len; ++i) {
       
   377                 char c = s.charAt(i);
       
   378                 if (!isTokenChar(c)) {
       
   379                     return false;
       
   380                 }
       
   381             }
       
   382             return true;
       
   383         } else {
       
   384             return false;
       
   385         }
       
   386     }
       
   387 
       
   388     /**
       
   389      * A string that holds all the special chars.
       
   390      */
       
   391 
       
   392     private static final String TSPECIALS = "()<>@,;:\\\"/[]?=";
       
   393 
       
   394 } // class MimeType