001/* MimeTypeParameterList.java -- Handle a list of MIME type parameters. 002 Copyright (C) 2004 Free Software Foundation, Inc. 003 004This file is part of GNU Classpath. 005 006GNU Classpath is free software; you can redistribute it and/or modify 007it under the terms of the GNU General Public License as published by 008the Free Software Foundation; either version 2, or (at your option) 009any later version. 010 011GNU Classpath is distributed in the hope that it will be useful, but 012WITHOUT ANY WARRANTY; without even the implied warranty of 013MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014General Public License for more details. 015 016You should have received a copy of the GNU General Public License 017along with GNU Classpath; see the file COPYING. If not, write to the 018Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 01902110-1301 USA. 020 021Linking this library statically or dynamically with other modules is 022making a combined work based on this library. Thus, the terms and 023conditions of the GNU General Public License cover the whole 024combination. 025 026As a special exception, the copyright holders of this library give you 027permission to link this library with independent modules to produce an 028executable, regardless of the license terms of these independent 029modules, and to copy and distribute the resulting executable under 030terms of your choice, provided that you also meet, for each linked 031independent module, the terms and conditions of the license of that 032module. An independent module is a module which is not derived from 033or based on this library. If you modify this library, you may extend 034this exception to your version of the library, but you are not 035obligated to do so. If you do not wish to do so, delete this 036exception statement from your version. */ 037 038package javax.activation; 039 040import gnu.java.lang.CPStringBuilder; 041 042import java.util.ArrayList; 043import java.util.Enumeration; 044import java.util.HashMap; 045import java.util.Iterator; 046import java.util.List; 047import java.util.Map; 048 049/** 050 * A list of MIME type parameters, as specified in RFCs 2045 and 2046. 051 * 052 * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a> 053 * @version 1.1 054 */ 055public class MimeTypeParameterList 056{ 057 058 private final List<String> parameterNames; 059 private final Map<String,String> parameterValues; 060 061 /** 062 * Constructor for an empty parameter list. 063 */ 064 public MimeTypeParameterList() 065 { 066 parameterNames = new ArrayList<String>(); 067 parameterValues = new HashMap<String,String>(); 068 } 069 070 /** 071 * Constructor that parses the specified MIME parameter data. 072 * @param parameterList a MIME parameter list string representation 073 */ 074 public MimeTypeParameterList(String parameterList) 075 throws MimeTypeParseException 076 { 077 this(); 078 parse(parameterList); 079 } 080 081 /** 082 * Parses the specified MIME parameter data, storing the results in this 083 * object. 084 * @param parameterList a MIME parameter list string representation 085 */ 086 protected void parse(String parameterList) 087 throws MimeTypeParseException 088 { 089 if (parameterList == null) 090 { 091 return; 092 } 093 // Tokenize list into parameters 094 char[] chars = parameterList.toCharArray(); 095 int len = chars.length; 096 boolean inQuotedString = false; 097 CPStringBuilder buffer = new CPStringBuilder(); 098 List<String> params = new ArrayList<String>(); 099 for (int i = 0; i < len; i++) 100 { 101 char c = chars[i]; 102 if (c == ';' && !inQuotedString) 103 { 104 String param = buffer.toString().trim(); 105 if (param.length() > 0) 106 { 107 params.add(param); 108 } 109 buffer.setLength(0); 110 } 111 else 112 { 113 if (c == '"') 114 { 115 inQuotedString = !inQuotedString; 116 } 117 buffer.append(c); 118 } 119 } 120 String param = buffer.toString().trim(); 121 if (param.length() > 0) 122 { 123 params.add(param); 124 } 125 126 // Tokenize each parameter into name + value 127 for (Iterator<String> i = params.iterator(); i.hasNext();) 128 { 129 param = i.next(); 130 int ei = param.indexOf('='); 131 if (ei == -1) 132 { 133 throw new MimeTypeParseException("Couldn't find the '=' that " + 134 "separates a parameter name " + 135 "from its value."); 136 } 137 String name = param.substring(0, ei).trim(); 138 MimeType.checkValidity(name, "Parameter name is invalid"); 139 String value = param.substring(ei + 1).trim(); 140 len = value.length(); 141 if (len > 1 && value.charAt(0) == '"' && 142 value.charAt(len - 1) == '"') 143 { 144 value = unquote(value); 145 } 146 else 147 { 148 MimeType.checkValidity(name, "Parameter value is invalid"); 149 } 150 151 parameterNames.add(name); 152 parameterValues.put(name.toLowerCase(), value); 153 } 154 } 155 156 /** 157 * Returns the number of parameters. 158 */ 159 public synchronized int size() 160 { 161 return parameterNames.size(); 162 } 163 164 /** 165 * Indicates if there are no parameters. 166 */ 167 public synchronized boolean isEmpty() 168 { 169 return parameterNames.isEmpty(); 170 } 171 172 /** 173 * Returns the value for the specified parameter name. 174 * @param name the parameter name 175 */ 176 public synchronized String get(String name) 177 { 178 name = name.trim(); 179 return parameterValues.get(name.toLowerCase()); 180 } 181 182 /** 183 * Sets the value for the specified parameter name. 184 * @param name the parameter name 185 * @param value the parameter value 186 */ 187 public synchronized void set(String name, String value) 188 { 189 name = name.trim(); 190 boolean exists = false; 191 for (String pname : parameterNames) 192 { 193 if (name.equalsIgnoreCase(pname)) 194 { 195 exists = true; 196 } 197 } 198 if (!exists) 199 { 200 parameterNames.add(name); 201 } 202 parameterValues.put(name.toLowerCase(), value); 203 } 204 205 /** 206 * Removes the parameter identified by the specified name. 207 * @param name the parameter name 208 */ 209 public synchronized void remove(String name) 210 { 211 name = name.trim(); 212 for (Iterator<String> i = parameterNames.iterator();i.hasNext();) 213 { 214 String pname = i.next(); 215 if (name.equalsIgnoreCase(pname)) 216 { 217 i.remove(); 218 } 219 } 220 parameterValues.remove(name.toLowerCase()); 221 } 222 223 /** 224 * Returns an enumeration of all the parameter names. 225 */ 226 // Raw type is forced by public spec. 227 @SuppressWarnings("unchecked") 228 public synchronized Enumeration getNames() 229 { 230 return new IteratorEnumeration(parameterNames.iterator()); 231 } 232 233 /** 234 * Returns an RFC 2045-compliant string representation of this parameter 235 * list. 236 */ 237 public synchronized String toString() 238 { 239 CPStringBuilder buffer = new CPStringBuilder(); 240 for (String name : parameterNames) 241 { 242 String value = parameterValues.get(name.toLowerCase()); 243 244 buffer.append(';'); 245 buffer.append(' '); 246 buffer.append(name); 247 buffer.append('='); 248 buffer.append(quote(value)); 249 } 250 return buffer.toString(); 251 } 252 253 private static String quote(String value) 254 { 255 boolean needsQuoting = false; 256 int len = value.length(); 257 for (int i = 0; i < len; i++) 258 { 259 if (!MimeType.isValidChar(value.charAt(i))) 260 { 261 needsQuoting = true; 262 break; 263 } 264 } 265 266 if (needsQuoting) 267 { 268 CPStringBuilder buffer = new CPStringBuilder(); 269 buffer.append('"'); 270 for (int i = 0; i < len; i++) 271 { 272 char c = value.charAt(i); 273 if (c == '\\' || c == '"') 274 { 275 buffer.append('\\'); 276 } 277 buffer.append(c); 278 } 279 buffer.append('"'); 280 return buffer.toString(); 281 } 282 return value; 283 } 284 285 private static String unquote(String value) 286 { 287 int len = value.length(); 288 CPStringBuilder buffer = new CPStringBuilder(); 289 for (int i = 1; i < len - 1; i++) 290 { 291 char c = value.charAt(i); 292 if (c == '\\') 293 { 294 i++; 295 if (i < len - 1) 296 { 297 c = value.charAt(i); 298 if (c != '\\' && c != '"') 299 { 300 buffer.append('\\'); 301 } 302 } 303 } 304 buffer.append(c); 305 } 306 return buffer.toString(); 307 } 308 309 /** 310 * Enumeration proxy for an Iterator. 311 */ 312 static class IteratorEnumeration 313 implements Enumeration<String> 314 { 315 316 final Iterator<String> iterator; 317 318 IteratorEnumeration(Iterator<String> iterator) 319 { 320 this.iterator = iterator; 321 } 322 323 public boolean hasMoreElements() 324 { 325 return iterator.hasNext(); 326 } 327 328 public String nextElement() 329 { 330 return iterator.next(); 331 } 332 333 } 334 335}