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    }