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}
336