jdk/src/java.desktop/share/classes/java/awt/datatransfer/MimeTypeParameterList.java
changeset 29042 7545059c977e
parent 29041 cda0ffc99002
parent 29038 c2058b635c17
child 29044 37c6512bd1df
equal deleted inserted replaced
29041:cda0ffc99002 29042:7545059c977e
     1 /*
       
     2  * Copyright (c) 1997, 2013, 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.util.Enumeration;
       
    29 import java.util.Hashtable;
       
    30 import java.util.Iterator;
       
    31 import java.util.Map;
       
    32 import java.util.Set;
       
    33 
       
    34 
       
    35 /**
       
    36  * An object that encapsulates the parameter list of a MimeType
       
    37  * as defined in RFC 2045 and 2046.
       
    38  *
       
    39  * @author jeff.dunn@eng.sun.com
       
    40  */
       
    41 class MimeTypeParameterList implements Cloneable {
       
    42 
       
    43     /**
       
    44      * Default constructor.
       
    45      */
       
    46     public MimeTypeParameterList() {
       
    47         parameters = new Hashtable<>();
       
    48     }
       
    49 
       
    50     public MimeTypeParameterList(String rawdata)
       
    51         throws MimeTypeParseException
       
    52     {
       
    53         parameters = new Hashtable<>();
       
    54 
       
    55         //    now parse rawdata
       
    56         parse(rawdata);
       
    57     }
       
    58 
       
    59     public int hashCode() {
       
    60         int code = Integer.MAX_VALUE/45; // "random" value for empty lists
       
    61         String paramName = null;
       
    62         Enumeration<String> enum_ = this.getNames();
       
    63 
       
    64         while (enum_.hasMoreElements()) {
       
    65             paramName = enum_.nextElement();
       
    66             code += paramName.hashCode();
       
    67             code += this.get(paramName).hashCode();
       
    68         }
       
    69 
       
    70         return code;
       
    71     } // hashCode()
       
    72 
       
    73     /**
       
    74      * Two parameter lists are considered equal if they have exactly
       
    75      * the same set of parameter names and associated values. The
       
    76      * order of the parameters is not considered.
       
    77      */
       
    78     public boolean equals(Object thatObject) {
       
    79         //System.out.println("MimeTypeParameterList.equals("+this+","+thatObject+")");
       
    80         if (!(thatObject instanceof MimeTypeParameterList)) {
       
    81             return false;
       
    82         }
       
    83         MimeTypeParameterList that = (MimeTypeParameterList)thatObject;
       
    84         if (this.size() != that.size()) {
       
    85             return false;
       
    86         }
       
    87         String name = null;
       
    88         String thisValue = null;
       
    89         String thatValue = null;
       
    90         Set<Map.Entry<String, String>> entries = parameters.entrySet();
       
    91         Iterator<Map.Entry<String, String>> iterator = entries.iterator();
       
    92         Map.Entry<String, String> entry = null;
       
    93         while (iterator.hasNext()) {
       
    94             entry = iterator.next();
       
    95             name = entry.getKey();
       
    96             thisValue = entry.getValue();
       
    97             thatValue = that.parameters.get(name);
       
    98             if ((thisValue == null) || (thatValue == null)) {
       
    99                 // both null -> equal, only one null -> not equal
       
   100                 if (thisValue != thatValue) {
       
   101                     return false;
       
   102                 }
       
   103             } else if (!thisValue.equals(thatValue)) {
       
   104                 return false;
       
   105             }
       
   106         } // while iterator
       
   107 
       
   108         return true;
       
   109     } // equals()
       
   110 
       
   111     /**
       
   112      * A routine for parsing the parameter list out of a String.
       
   113      */
       
   114     protected void parse(String rawdata) throws MimeTypeParseException {
       
   115         int length = rawdata.length();
       
   116         if(length > 0) {
       
   117             int currentIndex = skipWhiteSpace(rawdata, 0);
       
   118             int lastIndex = 0;
       
   119 
       
   120             if(currentIndex < length) {
       
   121                 char currentChar = rawdata.charAt(currentIndex);
       
   122                 while ((currentIndex < length) && (currentChar == ';')) {
       
   123                     String name;
       
   124                     String value;
       
   125                     boolean foundit;
       
   126 
       
   127                     //    eat the ';'
       
   128                     ++currentIndex;
       
   129 
       
   130                     //    now parse the parameter name
       
   131 
       
   132                     //    skip whitespace
       
   133                     currentIndex = skipWhiteSpace(rawdata, currentIndex);
       
   134 
       
   135                     if(currentIndex < length) {
       
   136                         //    find the end of the token char run
       
   137                         lastIndex = currentIndex;
       
   138                         currentChar = rawdata.charAt(currentIndex);
       
   139                         while((currentIndex < length) && isTokenChar(currentChar)) {
       
   140                             ++currentIndex;
       
   141                             currentChar = rawdata.charAt(currentIndex);
       
   142                         }
       
   143                         name = rawdata.substring(lastIndex, currentIndex).toLowerCase();
       
   144 
       
   145                         //    now parse the '=' that separates the name from the value
       
   146 
       
   147                         //    skip whitespace
       
   148                         currentIndex = skipWhiteSpace(rawdata, currentIndex);
       
   149 
       
   150                         if((currentIndex < length) && (rawdata.charAt(currentIndex) == '='))  {
       
   151                             //    eat it and parse the parameter value
       
   152                             ++currentIndex;
       
   153 
       
   154                             //    skip whitespace
       
   155                             currentIndex = skipWhiteSpace(rawdata, currentIndex);
       
   156 
       
   157                             if(currentIndex < length) {
       
   158                                 //    now find out whether or not we have a quoted value
       
   159                                 currentChar = rawdata.charAt(currentIndex);
       
   160                                 if(currentChar == '"') {
       
   161                                     //    yup it's quoted so eat it and capture the quoted string
       
   162                                     ++currentIndex;
       
   163                                     lastIndex = currentIndex;
       
   164 
       
   165                                     if(currentIndex < length) {
       
   166                                         //    find the next unescaped quote
       
   167                                         foundit = false;
       
   168                                         while((currentIndex < length) && !foundit) {
       
   169                                             currentChar = rawdata.charAt(currentIndex);
       
   170                                             if(currentChar == '\\') {
       
   171                                                 //    found an escape sequence so pass this and the next character
       
   172                                                 currentIndex += 2;
       
   173                                             } else if(currentChar == '"') {
       
   174                                                 //    found it!
       
   175                                                 foundit = true;
       
   176                                             } else {
       
   177                                                 ++currentIndex;
       
   178                                             }
       
   179                                         }
       
   180                                         if(currentChar == '"') {
       
   181                                             value = unquote(rawdata.substring(lastIndex, currentIndex));
       
   182                                             //    eat the quote
       
   183                                             ++currentIndex;
       
   184                                         } else {
       
   185                                             throw new MimeTypeParseException("Encountered unterminated quoted parameter value.");
       
   186                                         }
       
   187                                     } else {
       
   188                                         throw new MimeTypeParseException("Encountered unterminated quoted parameter value.");
       
   189                                     }
       
   190                                 } else if(isTokenChar(currentChar)) {
       
   191                                     //    nope it's an ordinary token so it ends with a non-token char
       
   192                                     lastIndex = currentIndex;
       
   193                                     foundit = false;
       
   194                                     while((currentIndex < length) && !foundit) {
       
   195                                         currentChar = rawdata.charAt(currentIndex);
       
   196 
       
   197                                         if(isTokenChar(currentChar)) {
       
   198                                             ++currentIndex;
       
   199                                         } else {
       
   200                                             foundit = true;
       
   201                                         }
       
   202                                     }
       
   203                                     value = rawdata.substring(lastIndex, currentIndex);
       
   204                                 } else {
       
   205                                     //    it ain't a value
       
   206                                     throw new MimeTypeParseException("Unexpected character encountered at index " + currentIndex);
       
   207                                 }
       
   208 
       
   209                                 //    now put the data into the hashtable
       
   210                                 parameters.put(name, value);
       
   211                             } else {
       
   212                                 throw new MimeTypeParseException("Couldn't find a value for parameter named " + name);
       
   213                             }
       
   214                         } else {
       
   215                             throw new MimeTypeParseException("Couldn't find the '=' that separates a parameter name from its value.");
       
   216                         }
       
   217                     } else {
       
   218                         throw new MimeTypeParseException("Couldn't find parameter name");
       
   219                     }
       
   220 
       
   221                     //    setup the next iteration
       
   222                     currentIndex = skipWhiteSpace(rawdata, currentIndex);
       
   223                     if(currentIndex < length) {
       
   224                         currentChar = rawdata.charAt(currentIndex);
       
   225                     }
       
   226                 }
       
   227                 if(currentIndex < length) {
       
   228                     throw new MimeTypeParseException("More characters encountered in input than expected.");
       
   229                 }
       
   230             }
       
   231         }
       
   232     }
       
   233 
       
   234     /**
       
   235      * return the number of name-value pairs in this list.
       
   236      */
       
   237     public int size() {
       
   238         return parameters.size();
       
   239     }
       
   240 
       
   241     /**
       
   242      * Determine whether or not this list is empty.
       
   243      */
       
   244     public boolean isEmpty() {
       
   245         return parameters.isEmpty();
       
   246     }
       
   247 
       
   248     /**
       
   249      * Retrieve the value associated with the given name, or null if there
       
   250      * is no current association.
       
   251      */
       
   252     public String get(String name) {
       
   253         return parameters.get(name.trim().toLowerCase());
       
   254     }
       
   255 
       
   256     /**
       
   257      * Set the value to be associated with the given name, replacing
       
   258      * any previous association.
       
   259      */
       
   260     public void set(String name, String value) {
       
   261         parameters.put(name.trim().toLowerCase(), value);
       
   262     }
       
   263 
       
   264     /**
       
   265      * Remove any value associated with the given name.
       
   266      */
       
   267     public void remove(String name) {
       
   268         parameters.remove(name.trim().toLowerCase());
       
   269     }
       
   270 
       
   271     /**
       
   272      * Retrieve an enumeration of all the names in this list.
       
   273      */
       
   274     public Enumeration<String> getNames() {
       
   275         return parameters.keys();
       
   276     }
       
   277 
       
   278     public String toString() {
       
   279         // Heuristic: 8 characters per field
       
   280         StringBuilder buffer = new StringBuilder(parameters.size() * 16);
       
   281 
       
   282         Enumeration<String> keys = parameters.keys();
       
   283         while(keys.hasMoreElements())
       
   284         {
       
   285             buffer.append("; ");
       
   286 
       
   287             String key = keys.nextElement();
       
   288             buffer.append(key);
       
   289             buffer.append('=');
       
   290                buffer.append(quote(parameters.get(key)));
       
   291         }
       
   292 
       
   293         return buffer.toString();
       
   294     }
       
   295 
       
   296     /**
       
   297      * @return a clone of this object
       
   298      */
       
   299     @SuppressWarnings("unchecked") // Cast from clone
       
   300      public Object clone() {
       
   301          MimeTypeParameterList newObj = null;
       
   302          try {
       
   303              newObj = (MimeTypeParameterList)super.clone();
       
   304          } catch (CloneNotSupportedException cannotHappen) {
       
   305          }
       
   306          newObj.parameters = (Hashtable<String, String>)parameters.clone();
       
   307          return newObj;
       
   308      }
       
   309 
       
   310     private Hashtable<String, String> parameters;
       
   311 
       
   312     //    below here be scary parsing related things
       
   313 
       
   314     /**
       
   315      * Determine whether or not a given character belongs to a legal token.
       
   316      */
       
   317     private static boolean isTokenChar(char c) {
       
   318         return ((c > 040) && (c < 0177)) && (TSPECIALS.indexOf(c) < 0);
       
   319     }
       
   320 
       
   321     /**
       
   322      * return the index of the first non white space character in
       
   323      * rawdata at or after index i.
       
   324      */
       
   325     private static int skipWhiteSpace(String rawdata, int i) {
       
   326         int length = rawdata.length();
       
   327         if (i < length) {
       
   328             char c =  rawdata.charAt(i);
       
   329             while ((i < length) && Character.isWhitespace(c)) {
       
   330                 ++i;
       
   331                 c = rawdata.charAt(i);
       
   332             }
       
   333         }
       
   334 
       
   335         return i;
       
   336     }
       
   337 
       
   338     /**
       
   339      * A routine that knows how and when to quote and escape the given value.
       
   340      */
       
   341     private static String quote(String value) {
       
   342         boolean needsQuotes = false;
       
   343 
       
   344         //    check to see if we actually have to quote this thing
       
   345         int length = value.length();
       
   346         for(int i = 0; (i < length) && !needsQuotes; ++i) {
       
   347             needsQuotes = !isTokenChar(value.charAt(i));
       
   348         }
       
   349 
       
   350         if(needsQuotes) {
       
   351             StringBuilder buffer = new StringBuilder((int)(length * 1.5));
       
   352 
       
   353             //    add the initial quote
       
   354             buffer.append('"');
       
   355 
       
   356             //    add the properly escaped text
       
   357             for(int i = 0; i < length; ++i) {
       
   358                 char c = value.charAt(i);
       
   359                 if((c == '\\') || (c == '"')) {
       
   360                     buffer.append('\\');
       
   361                 }
       
   362                 buffer.append(c);
       
   363             }
       
   364 
       
   365             //    add the closing quote
       
   366             buffer.append('"');
       
   367 
       
   368             return buffer.toString();
       
   369         }
       
   370         else
       
   371         {
       
   372             return value;
       
   373         }
       
   374     }
       
   375 
       
   376     /**
       
   377      * A routine that knows how to strip the quotes and escape sequences from the given value.
       
   378      */
       
   379     private static String unquote(String value) {
       
   380         int valueLength = value.length();
       
   381         StringBuilder buffer = new StringBuilder(valueLength);
       
   382 
       
   383         boolean escaped = false;
       
   384         for(int i = 0; i < valueLength; ++i) {
       
   385             char currentChar = value.charAt(i);
       
   386             if(!escaped && (currentChar != '\\')) {
       
   387                 buffer.append(currentChar);
       
   388             } else if(escaped) {
       
   389                 buffer.append(currentChar);
       
   390                 escaped = false;
       
   391             } else {
       
   392                 escaped = true;
       
   393             }
       
   394         }
       
   395 
       
   396         return buffer.toString();
       
   397     }
       
   398 
       
   399     /**
       
   400      * A string that holds all the special chars.
       
   401      */
       
   402     private static final String TSPECIALS = "()<>@,;:\\\"/[]?=";
       
   403 
       
   404 }