Wed, 27 Apr 2016 15:32:20 -0700
Added tag jdk8u102-b05 for changeset 1f512419ae33
aoqi@0 | 1 | /* |
aoqi@0 | 2 | * Copyright (c) 1997, 2005, Oracle and/or its affiliates. All rights reserved. |
aoqi@0 | 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
aoqi@0 | 4 | * |
aoqi@0 | 5 | * This code is free software; you can redistribute it and/or modify it |
aoqi@0 | 6 | * under the terms of the GNU General Public License version 2 only, as |
aoqi@0 | 7 | * published by the Free Software Foundation. Oracle designates this |
aoqi@0 | 8 | * particular file as subject to the "Classpath" exception as provided |
aoqi@0 | 9 | * by Oracle in the LICENSE file that accompanied this code. |
aoqi@0 | 10 | * |
aoqi@0 | 11 | * This code is distributed in the hope that it will be useful, but WITHOUT |
aoqi@0 | 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
aoqi@0 | 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
aoqi@0 | 14 | * version 2 for more details (a copy is included in the LICENSE file that |
aoqi@0 | 15 | * accompanied this code). |
aoqi@0 | 16 | * |
aoqi@0 | 17 | * You should have received a copy of the GNU General Public License version |
aoqi@0 | 18 | * 2 along with this work; if not, write to the Free Software Foundation, |
aoqi@0 | 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
aoqi@0 | 20 | * |
aoqi@0 | 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
aoqi@0 | 22 | * or visit www.oracle.com if you need additional information or have any |
aoqi@0 | 23 | * questions. |
aoqi@0 | 24 | */ |
aoqi@0 | 25 | |
aoqi@0 | 26 | package javax.activation; |
aoqi@0 | 27 | |
aoqi@0 | 28 | import java.util.Hashtable; |
aoqi@0 | 29 | import java.util.Enumeration; |
aoqi@0 | 30 | import java.util.Locale; |
aoqi@0 | 31 | |
aoqi@0 | 32 | /** |
aoqi@0 | 33 | * A parameter list of a MimeType |
aoqi@0 | 34 | * as defined in RFC 2045 and 2046. The Primary type of the |
aoqi@0 | 35 | * object must already be stripped off. |
aoqi@0 | 36 | * |
aoqi@0 | 37 | * @see javax.activation.MimeType |
aoqi@0 | 38 | * |
aoqi@0 | 39 | * @since 1.6 |
aoqi@0 | 40 | */ |
aoqi@0 | 41 | public class MimeTypeParameterList { |
aoqi@0 | 42 | private Hashtable parameters; |
aoqi@0 | 43 | |
aoqi@0 | 44 | /** |
aoqi@0 | 45 | * A string that holds all the special chars. |
aoqi@0 | 46 | */ |
aoqi@0 | 47 | private static final String TSPECIALS = "()<>@,;:/[]?=\\\""; |
aoqi@0 | 48 | |
aoqi@0 | 49 | |
aoqi@0 | 50 | /** |
aoqi@0 | 51 | * Default constructor. |
aoqi@0 | 52 | */ |
aoqi@0 | 53 | public MimeTypeParameterList() { |
aoqi@0 | 54 | parameters = new Hashtable(); |
aoqi@0 | 55 | } |
aoqi@0 | 56 | |
aoqi@0 | 57 | /** |
aoqi@0 | 58 | * Constructs a new MimeTypeParameterList with the passed in data. |
aoqi@0 | 59 | * |
aoqi@0 | 60 | * @param parameterList an RFC 2045, 2046 compliant parameter list. |
aoqi@0 | 61 | */ |
aoqi@0 | 62 | public MimeTypeParameterList(String parameterList) |
aoqi@0 | 63 | throws MimeTypeParseException { |
aoqi@0 | 64 | parameters = new Hashtable(); |
aoqi@0 | 65 | |
aoqi@0 | 66 | // now parse rawdata |
aoqi@0 | 67 | parse(parameterList); |
aoqi@0 | 68 | } |
aoqi@0 | 69 | |
aoqi@0 | 70 | /** |
aoqi@0 | 71 | * A routine for parsing the parameter list out of a String. |
aoqi@0 | 72 | * |
aoqi@0 | 73 | * @param parameterList an RFC 2045, 2046 compliant parameter list. |
aoqi@0 | 74 | */ |
aoqi@0 | 75 | protected void parse(String parameterList) throws MimeTypeParseException { |
aoqi@0 | 76 | if (parameterList == null) |
aoqi@0 | 77 | return; |
aoqi@0 | 78 | |
aoqi@0 | 79 | int length = parameterList.length(); |
aoqi@0 | 80 | if (length <= 0) |
aoqi@0 | 81 | return; |
aoqi@0 | 82 | |
aoqi@0 | 83 | int i; |
aoqi@0 | 84 | char c; |
aoqi@0 | 85 | for (i = skipWhiteSpace(parameterList, 0); |
aoqi@0 | 86 | i < length && (c = parameterList.charAt(i)) == ';'; |
aoqi@0 | 87 | i = skipWhiteSpace(parameterList, i)) { |
aoqi@0 | 88 | int lastIndex; |
aoqi@0 | 89 | String name; |
aoqi@0 | 90 | String value; |
aoqi@0 | 91 | |
aoqi@0 | 92 | // eat the ';' |
aoqi@0 | 93 | i++; |
aoqi@0 | 94 | |
aoqi@0 | 95 | // now parse the parameter name |
aoqi@0 | 96 | |
aoqi@0 | 97 | // skip whitespace |
aoqi@0 | 98 | i = skipWhiteSpace(parameterList, i); |
aoqi@0 | 99 | |
aoqi@0 | 100 | // tolerate trailing semicolon, even though it violates the spec |
aoqi@0 | 101 | if (i >= length) |
aoqi@0 | 102 | return; |
aoqi@0 | 103 | |
aoqi@0 | 104 | // find the end of the token char run |
aoqi@0 | 105 | lastIndex = i; |
aoqi@0 | 106 | while ((i < length) && isTokenChar(parameterList.charAt(i))) |
aoqi@0 | 107 | i++; |
aoqi@0 | 108 | |
aoqi@0 | 109 | name = parameterList.substring(lastIndex, i). |
aoqi@0 | 110 | toLowerCase(Locale.ENGLISH); |
aoqi@0 | 111 | |
aoqi@0 | 112 | // now parse the '=' that separates the name from the value |
aoqi@0 | 113 | i = skipWhiteSpace(parameterList, i); |
aoqi@0 | 114 | |
aoqi@0 | 115 | if (i >= length || parameterList.charAt(i) != '=') |
aoqi@0 | 116 | throw new MimeTypeParseException( |
aoqi@0 | 117 | "Couldn't find the '=' that separates a " + |
aoqi@0 | 118 | "parameter name from its value."); |
aoqi@0 | 119 | |
aoqi@0 | 120 | // eat it and parse the parameter value |
aoqi@0 | 121 | i++; |
aoqi@0 | 122 | i = skipWhiteSpace(parameterList, i); |
aoqi@0 | 123 | |
aoqi@0 | 124 | if (i >= length) |
aoqi@0 | 125 | throw new MimeTypeParseException( |
aoqi@0 | 126 | "Couldn't find a value for parameter named " + name); |
aoqi@0 | 127 | |
aoqi@0 | 128 | // now find out whether or not we have a quoted value |
aoqi@0 | 129 | c = parameterList.charAt(i); |
aoqi@0 | 130 | if (c == '"') { |
aoqi@0 | 131 | // yup it's quoted so eat it and capture the quoted string |
aoqi@0 | 132 | i++; |
aoqi@0 | 133 | if (i >= length) |
aoqi@0 | 134 | throw new MimeTypeParseException( |
aoqi@0 | 135 | "Encountered unterminated quoted parameter value."); |
aoqi@0 | 136 | |
aoqi@0 | 137 | lastIndex = i; |
aoqi@0 | 138 | |
aoqi@0 | 139 | // find the next unescaped quote |
aoqi@0 | 140 | while (i < length) { |
aoqi@0 | 141 | c = parameterList.charAt(i); |
aoqi@0 | 142 | if (c == '"') |
aoqi@0 | 143 | break; |
aoqi@0 | 144 | if (c == '\\') { |
aoqi@0 | 145 | // found an escape sequence |
aoqi@0 | 146 | // so skip this and the |
aoqi@0 | 147 | // next character |
aoqi@0 | 148 | i++; |
aoqi@0 | 149 | } |
aoqi@0 | 150 | i++; |
aoqi@0 | 151 | } |
aoqi@0 | 152 | if (c != '"') |
aoqi@0 | 153 | throw new MimeTypeParseException( |
aoqi@0 | 154 | "Encountered unterminated quoted parameter value."); |
aoqi@0 | 155 | |
aoqi@0 | 156 | value = unquote(parameterList.substring(lastIndex, i)); |
aoqi@0 | 157 | // eat the quote |
aoqi@0 | 158 | i++; |
aoqi@0 | 159 | } else if (isTokenChar(c)) { |
aoqi@0 | 160 | // nope it's an ordinary token so it |
aoqi@0 | 161 | // ends with a non-token char |
aoqi@0 | 162 | lastIndex = i; |
aoqi@0 | 163 | while (i < length && isTokenChar(parameterList.charAt(i))) |
aoqi@0 | 164 | i++; |
aoqi@0 | 165 | value = parameterList.substring(lastIndex, i); |
aoqi@0 | 166 | } else { |
aoqi@0 | 167 | // it ain't a value |
aoqi@0 | 168 | throw new MimeTypeParseException( |
aoqi@0 | 169 | "Unexpected character encountered at index " + i); |
aoqi@0 | 170 | } |
aoqi@0 | 171 | |
aoqi@0 | 172 | // now put the data into the hashtable |
aoqi@0 | 173 | parameters.put(name, value); |
aoqi@0 | 174 | } |
aoqi@0 | 175 | if (i < length) { |
aoqi@0 | 176 | throw new MimeTypeParseException( |
aoqi@0 | 177 | "More characters encountered in input than expected."); |
aoqi@0 | 178 | } |
aoqi@0 | 179 | } |
aoqi@0 | 180 | |
aoqi@0 | 181 | /** |
aoqi@0 | 182 | * Return the number of name-value pairs in this list. |
aoqi@0 | 183 | * |
aoqi@0 | 184 | * @return the number of parameters |
aoqi@0 | 185 | */ |
aoqi@0 | 186 | public int size() { |
aoqi@0 | 187 | return parameters.size(); |
aoqi@0 | 188 | } |
aoqi@0 | 189 | |
aoqi@0 | 190 | /** |
aoqi@0 | 191 | * Determine whether or not this list is empty. |
aoqi@0 | 192 | * |
aoqi@0 | 193 | * @return true if there are no parameters |
aoqi@0 | 194 | */ |
aoqi@0 | 195 | public boolean isEmpty() { |
aoqi@0 | 196 | return parameters.isEmpty(); |
aoqi@0 | 197 | } |
aoqi@0 | 198 | |
aoqi@0 | 199 | /** |
aoqi@0 | 200 | * Retrieve the value associated with the given name, or null if there |
aoqi@0 | 201 | * is no current association. |
aoqi@0 | 202 | * |
aoqi@0 | 203 | * @param name the parameter name |
aoqi@0 | 204 | * @return the parameter's value |
aoqi@0 | 205 | */ |
aoqi@0 | 206 | public String get(String name) { |
aoqi@0 | 207 | return (String)parameters.get(name.trim().toLowerCase(Locale.ENGLISH)); |
aoqi@0 | 208 | } |
aoqi@0 | 209 | |
aoqi@0 | 210 | /** |
aoqi@0 | 211 | * Set the value to be associated with the given name, replacing |
aoqi@0 | 212 | * any previous association. |
aoqi@0 | 213 | * |
aoqi@0 | 214 | * @param name the parameter name |
aoqi@0 | 215 | * @param value the parameter's value |
aoqi@0 | 216 | */ |
aoqi@0 | 217 | public void set(String name, String value) { |
aoqi@0 | 218 | parameters.put(name.trim().toLowerCase(Locale.ENGLISH), value); |
aoqi@0 | 219 | } |
aoqi@0 | 220 | |
aoqi@0 | 221 | /** |
aoqi@0 | 222 | * Remove any value associated with the given name. |
aoqi@0 | 223 | * |
aoqi@0 | 224 | * @param name the parameter name |
aoqi@0 | 225 | */ |
aoqi@0 | 226 | public void remove(String name) { |
aoqi@0 | 227 | parameters.remove(name.trim().toLowerCase(Locale.ENGLISH)); |
aoqi@0 | 228 | } |
aoqi@0 | 229 | |
aoqi@0 | 230 | /** |
aoqi@0 | 231 | * Retrieve an enumeration of all the names in this list. |
aoqi@0 | 232 | * |
aoqi@0 | 233 | * @return an enumeration of all parameter names |
aoqi@0 | 234 | */ |
aoqi@0 | 235 | public Enumeration getNames() { |
aoqi@0 | 236 | return parameters.keys(); |
aoqi@0 | 237 | } |
aoqi@0 | 238 | |
aoqi@0 | 239 | /** |
aoqi@0 | 240 | * Return a string representation of this object. |
aoqi@0 | 241 | */ |
aoqi@0 | 242 | public String toString() { |
aoqi@0 | 243 | StringBuffer buffer = new StringBuffer(); |
aoqi@0 | 244 | buffer.ensureCapacity(parameters.size() * 16); |
aoqi@0 | 245 | // heuristic: 8 characters per field |
aoqi@0 | 246 | |
aoqi@0 | 247 | Enumeration keys = parameters.keys(); |
aoqi@0 | 248 | while (keys.hasMoreElements()) { |
aoqi@0 | 249 | String key = (String)keys.nextElement(); |
aoqi@0 | 250 | buffer.append("; "); |
aoqi@0 | 251 | buffer.append(key); |
aoqi@0 | 252 | buffer.append('='); |
aoqi@0 | 253 | buffer.append(quote((String)parameters.get(key))); |
aoqi@0 | 254 | } |
aoqi@0 | 255 | |
aoqi@0 | 256 | return buffer.toString(); |
aoqi@0 | 257 | } |
aoqi@0 | 258 | |
aoqi@0 | 259 | // below here be scary parsing related things |
aoqi@0 | 260 | |
aoqi@0 | 261 | /** |
aoqi@0 | 262 | * Determine whether or not a given character belongs to a legal token. |
aoqi@0 | 263 | */ |
aoqi@0 | 264 | private static boolean isTokenChar(char c) { |
aoqi@0 | 265 | return ((c > 040) && (c < 0177)) && (TSPECIALS.indexOf(c) < 0); |
aoqi@0 | 266 | } |
aoqi@0 | 267 | |
aoqi@0 | 268 | /** |
aoqi@0 | 269 | * return the index of the first non white space character in |
aoqi@0 | 270 | * rawdata at or after index i. |
aoqi@0 | 271 | */ |
aoqi@0 | 272 | private static int skipWhiteSpace(String rawdata, int i) { |
aoqi@0 | 273 | int length = rawdata.length(); |
aoqi@0 | 274 | while ((i < length) && Character.isWhitespace(rawdata.charAt(i))) |
aoqi@0 | 275 | i++; |
aoqi@0 | 276 | return i; |
aoqi@0 | 277 | } |
aoqi@0 | 278 | |
aoqi@0 | 279 | /** |
aoqi@0 | 280 | * A routine that knows how and when to quote and escape the given value. |
aoqi@0 | 281 | */ |
aoqi@0 | 282 | private static String quote(String value) { |
aoqi@0 | 283 | boolean needsQuotes = false; |
aoqi@0 | 284 | |
aoqi@0 | 285 | // check to see if we actually have to quote this thing |
aoqi@0 | 286 | int length = value.length(); |
aoqi@0 | 287 | for (int i = 0; (i < length) && !needsQuotes; i++) { |
aoqi@0 | 288 | needsQuotes = !isTokenChar(value.charAt(i)); |
aoqi@0 | 289 | } |
aoqi@0 | 290 | |
aoqi@0 | 291 | if (needsQuotes) { |
aoqi@0 | 292 | StringBuffer buffer = new StringBuffer(); |
aoqi@0 | 293 | buffer.ensureCapacity((int)(length * 1.5)); |
aoqi@0 | 294 | |
aoqi@0 | 295 | // add the initial quote |
aoqi@0 | 296 | buffer.append('"'); |
aoqi@0 | 297 | |
aoqi@0 | 298 | // add the properly escaped text |
aoqi@0 | 299 | for (int i = 0; i < length; ++i) { |
aoqi@0 | 300 | char c = value.charAt(i); |
aoqi@0 | 301 | if ((c == '\\') || (c == '"')) |
aoqi@0 | 302 | buffer.append('\\'); |
aoqi@0 | 303 | buffer.append(c); |
aoqi@0 | 304 | } |
aoqi@0 | 305 | |
aoqi@0 | 306 | // add the closing quote |
aoqi@0 | 307 | buffer.append('"'); |
aoqi@0 | 308 | |
aoqi@0 | 309 | return buffer.toString(); |
aoqi@0 | 310 | } else { |
aoqi@0 | 311 | return value; |
aoqi@0 | 312 | } |
aoqi@0 | 313 | } |
aoqi@0 | 314 | |
aoqi@0 | 315 | /** |
aoqi@0 | 316 | * A routine that knows how to strip the quotes and |
aoqi@0 | 317 | * escape sequences from the given value. |
aoqi@0 | 318 | */ |
aoqi@0 | 319 | private static String unquote(String value) { |
aoqi@0 | 320 | int valueLength = value.length(); |
aoqi@0 | 321 | StringBuffer buffer = new StringBuffer(); |
aoqi@0 | 322 | buffer.ensureCapacity(valueLength); |
aoqi@0 | 323 | |
aoqi@0 | 324 | boolean escaped = false; |
aoqi@0 | 325 | for (int i = 0; i < valueLength; ++i) { |
aoqi@0 | 326 | char currentChar = value.charAt(i); |
aoqi@0 | 327 | if (!escaped && (currentChar != '\\')) { |
aoqi@0 | 328 | buffer.append(currentChar); |
aoqi@0 | 329 | } else if (escaped) { |
aoqi@0 | 330 | buffer.append(currentChar); |
aoqi@0 | 331 | escaped = false; |
aoqi@0 | 332 | } else { |
aoqi@0 | 333 | escaped = true; |
aoqi@0 | 334 | } |
aoqi@0 | 335 | } |
aoqi@0 | 336 | |
aoqi@0 | 337 | return buffer.toString(); |
aoqi@0 | 338 | } |
aoqi@0 | 339 | } |