001 /* MimeTypeParameterList.java -- Handle a list of MIME type parameters.
002 Copyright (C) 2004 Free Software Foundation, Inc.
003
004 This file is part of GNU Classpath.
005
006 GNU Classpath is free software; you can redistribute it and/or modify
007 it under the terms of the GNU General Public License as published by
008 the Free Software Foundation; either version 2, or (at your option)
009 any later version.
010
011 GNU Classpath is distributed in the hope that it will be useful, but
012 WITHOUT ANY WARRANTY; without even the implied warranty of
013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014 General Public License for more details.
015
016 You should have received a copy of the GNU General Public License
017 along with GNU Classpath; see the file COPYING. If not, write to the
018 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
019 02110-1301 USA.
020
021 Linking this library statically or dynamically with other modules is
022 making a combined work based on this library. Thus, the terms and
023 conditions of the GNU General Public License cover the whole
024 combination.
025
026 As a special exception, the copyright holders of this library give you
027 permission to link this library with independent modules to produce an
028 executable, regardless of the license terms of these independent
029 modules, and to copy and distribute the resulting executable under
030 terms of your choice, provided that you also meet, for each linked
031 independent module, the terms and conditions of the license of that
032 module. An independent module is a module which is not derived from
033 or based on this library. If you modify this library, you may extend
034 this exception to your version of the library, but you are not
035 obligated to do so. If you do not wish to do so, delete this
036 exception statement from your version. */
037
038 package javax.activation;
039
040 import gnu.java.lang.CPStringBuilder;
041
042 import java.util.ArrayList;
043 import java.util.Enumeration;
044 import java.util.HashMap;
045 import java.util.Iterator;
046 import java.util.List;
047 import 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 */
055 public 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 }