001    package org.apache.fulcrum.mimetype.util;
002    
003    
004    /*
005     * Licensed to the Apache Software Foundation (ASF) under one
006     * or more contributor license agreements.  See the NOTICE file
007     * distributed with this work for additional information
008     * regarding copyright ownership.  The ASF licenses this file
009     * to you under the Apache License, Version 2.0 (the
010     * "License"); you may not use this file except in compliance
011     * with the License.  You may obtain a copy of the License at
012     *
013     *   http://www.apache.org/licenses/LICENSE-2.0
014     *
015     * Unless required by applicable law or agreed to in writing,
016     * software distributed under the License is distributed on an
017     * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
018     * KIND, either express or implied.  See the License for the
019     * specific language governing permissions and limitations
020     * under the License.
021     */
022    
023    
024    import java.util.ArrayList;
025    
026    /**
027     * This class is used to represent parsed MIME types.
028     * The representation is parsed from a string based
029     * representation of the MIME type, as defined in the RFC1345.
030     *
031     * @author <a href="mailto:ilkka.priha@simsoft.fi">Ilkka Priha</a>
032     * @version $Id: MimeType.java 813677 2009-09-11 06:48:11Z tv $
033     */
034    public class MimeType
035        implements Cloneable
036    {
037        /**
038         * A list of well known MIME types.
039         */
040        public static MimeType TEXT_HTML;
041        public static MimeType TEXT_WML;
042        public static MimeType TEXT_HDML;
043        public static MimeType TEXT_CHTML;
044        public static MimeType TEXT_PLAIN;
045        public static MimeType MULTIPART;
046        public static MimeType MULTIPART_FORM_DATA;
047        public static MimeType APPLICATION_POSTSCRIPT;
048        public static MimeType APPLICATION_OCTET_STREAM;
049        public static MimeType APPLICATION_X_JAVA_AGENT;
050        public static MimeType APPLICATION_X_WWW_FORM_URLENCODED;
051        public static MimeType MESSAGE_HTTP;
052        public static MimeType TEXT_CSS;
053        public static MimeType TEXT;
054        public static MimeType IMAGE_PNG;
055        public static MimeType IMAGE_GIF;
056        public static MimeType IMAGE_JPEG;
057        public static MimeType IMAGE_WBMP;
058        static
059        {
060            TEXT_HTML =
061                new MimeType("text/html");
062            TEXT_WML =
063                new MimeType("text/vnd.wap.wml");
064            TEXT_HDML =
065                new MimeType("text/x-hdml");
066            TEXT_CHTML =
067                new MimeType("text/x-chtml");
068            TEXT_PLAIN =
069                new MimeType("text/plain");
070            MULTIPART       =
071                new MimeType("multipart/*");
072            MULTIPART_FORM_DATA     =
073                new MimeType("multipart/form-data");
074            APPLICATION_POSTSCRIPT =
075                new MimeType("application/postscript");
076            APPLICATION_OCTET_STREAM =
077                new MimeType("application/octet-stream");
078            APPLICATION_X_JAVA_AGENT =
079                new MimeType("application/x-java-agent");
080            APPLICATION_X_WWW_FORM_URLENCODED =
081                new MimeType("application/x-www-form-urlencoded");
082            MESSAGE_HTTP =
083                new MimeType("message/http");
084            TEXT_CSS =
085                new MimeType("text/css");
086            TEXT =
087                new MimeType("text/*");
088            IMAGE_PNG =
089                new MimeType("image/png");
090            IMAGE_GIF =
091                new MimeType("image/gif");
092            IMAGE_JPEG =
093                new MimeType("image/jpeg");
094            IMAGE_WBMP =
095                new MimeType("image/vnd.wap.wbmp");
096        }
097    
098        /**
099         * MIME type matching constants.
100         */
101        public static final int NO_MATCH = 0;
102        public static final int MATCH_TYPE = 1;
103        public static final int MATCH_SUBTYPE = 2;
104        public static final int MATCH_SPECIFIC_SUBTYPE = 3;
105    
106        /**
107         * A string representation of the main type.
108         */
109        private String mimeType;
110    
111        /**
112         * A string representation of the subtype.
113         */
114        private String mimeSubtype;
115    
116        /**
117         * Parameter names.
118         */
119        private String parameterNames[];
120    
121        /**
122         * Parameter values.
123         */
124        private String parameterValues[];
125    
126        /**
127         * A string representation of the MIME type.
128         */
129        private String mimeTypeString;
130    
131        /**
132         * Constructs a new MIME type by parsing a specification string.
133         *
134         * @param spec a string representing a MIME type.
135         * @throws IllegalArgumentException for parsing errors.
136         */
137        public MimeType(String spec)
138        {
139            this(spec,true);
140        }
141    
142        /**
143         * Constructs a new MIME type by parsing a specification string.
144         *
145         * @param spec a string representing a MIME type.
146         * @param parsep a flag for parsing parameters also.
147         * @throws IllegalArgumentException for parsing errors.
148         */
149        public MimeType(String spec,
150                        boolean parsep)
151        {
152            int start = 0;
153            char look = '\0';
154            int length = spec.length();
155    
156            // Skip leading/trailing blanks.
157            while ((start < length) &&
158                   Character.isWhitespace(spec.charAt(start)))
159            {
160                start++;
161            }
162            while ((length > start) &&
163                   Character.isWhitespace(spec.charAt(length - 1)))
164            {
165                length--;
166            }
167    
168            // Get the type.
169            StringBuffer sb = new StringBuffer();
170            while ((start < length) &&
171                   ((look = spec.charAt(start)) != '/'))
172            {
173                sb.append(look);
174                start++;
175            }
176            if (look != '/')
177            {
178                throw new IllegalArgumentException(
179                    "Syntax error in MIME type " + spec);
180            }
181            mimeType = sb.toString();
182    
183            // Get the subtype.
184            start++;
185            sb.setLength(0);
186            while ((start < length) &&
187                   ((look = spec.charAt(start)) != ';') &&
188                   !Character.isWhitespace(look))
189            {
190                sb.append(look);
191                start++;
192            }
193            mimeSubtype = sb.toString();
194    
195            if (parsep)
196            {
197                // Get parameters, if any.
198                while ((start < length) &&
199                       Character.isWhitespace(spec.charAt(start)))
200                {
201                    start++;
202                }
203                if (start < length)
204                {
205                    if (spec.charAt(start) != ';')
206                    {
207                        throw new IllegalArgumentException(
208                            "Syntax error in MIME type parameters " + spec);
209                    }
210                    start++;
211                    ArrayList na = new ArrayList(4);
212                    ArrayList va = new ArrayList(4);
213                    while (start < length)
214                    {
215                        // Get the name.
216                        while ((start < length) &&
217                               Character.isWhitespace(spec.charAt(start)))
218                        {
219                            start++;
220                        }
221                        sb.setLength(0);
222                        while ((start < length) &&
223                               ((look=spec.charAt(start)) != '=') &&
224                               !Character.isWhitespace(look))
225                        {
226                            sb.append(Character.toLowerCase(look));
227                            start++ ;
228                        }
229                        String name = sb.toString();
230    
231                        // Get the value.
232                        while ((start < length) &&
233                               Character.isWhitespace(spec.charAt(start)))
234                        {
235                            start++;
236                        }
237                        if (spec.charAt(start) != '=')
238                        {
239                            throw new IllegalArgumentException(
240                                "Syntax error in MIME type parameters " + spec);
241                        }
242                        start++ ;
243                        while ((start < length) &&
244                               Character.isWhitespace(spec.charAt(start)))
245                        {
246                            start++;
247                        }
248                        sb.setLength(0);
249                        char delim = ';';
250                        if (spec.charAt(start) == '"')
251                        {
252                            start++;
253                            delim = '"';
254                        }
255                        while ((start < length) &&
256                               ((look = spec.charAt(start)) != delim) &&
257                               ((delim == '"') ||
258                                !Character.isWhitespace(look)))
259                        {
260                            sb.append(look);
261                            start++;
262                        }
263                        while ((start < length) &&
264                               (spec.charAt(start) != ';'))
265                        {
266                            start++;
267                        }
268                        start++;
269                        String value = sb.toString();
270    
271                        na.add(name);
272                        va.add(value);
273                    }
274                    parameterNames = (String[]) na.toArray(new String[na.size()]);
275                    parameterValues = (String[]) va.toArray(new String[va.size()]);
276                }
277            }
278        }
279    
280        /**
281         * Contructs a new MIME type from specified types.
282         *
283         * @param type a type.
284         * @param subtype a subtype.
285         * @throws NullPointerException if type or subtype are nulls.
286         */
287        public MimeType(String type,
288                        String subtype)
289        {
290            this(type,subtype,null,null);
291        }
292    
293        /**
294         * Contructs a new MIME type from specified parameters.
295         *
296         * @param type a type.
297         * @param subtype a subtype.
298         * @param names parameters names.
299         * @param values parameter values.
300         * @throws NullPointerException if type or subtype are nulls.
301         */
302        public MimeType(String type,
303                        String subtype,
304                        String names[],
305                        String values[])
306        {
307            if ((type == null) ||
308                (subtype == null))
309            {
310                throw new NullPointerException("MIME type or subtype missing");
311            }
312            mimeType = type.trim();
313            mimeSubtype = subtype.trim();
314            parameterNames  = names;
315            parameterValues = values;
316        }
317    
318        /**
319         * Compares the specified MIME type to this one
320         * and returns a matching level:
321         * NO_MATCH=types do not match,
322         * MATCH_TYPE=types match,
323         * MATCH_SPECIFIC_TYPE=types match exactly,
324         * MATCH_SUBTYPE=types match, subtypes match too,
325         * MATCH_SPECIFIC_SUBTYPE=types match, subtypes match exactly.
326         *
327         * @param other the MimeType to compare.
328         * @return the matching level.
329         */
330        public int match(MimeType other)
331        {
332            if (mimeType.equals("*") ||
333                other.mimeType.equals("*"))
334            {
335                return MATCH_TYPE;
336            }
337            else if (!mimeType.equalsIgnoreCase(other.mimeType))
338            {
339                return NO_MATCH;
340            }
341            else if (mimeSubtype.equals("*") ||
342                     other.mimeSubtype.equals("*"))
343            {
344                return MATCH_SUBTYPE;
345            }
346            else if (!mimeSubtype.equalsIgnoreCase(other.mimeSubtype))
347            {
348                return NO_MATCH;
349            }
350            else
351            {
352                return MATCH_SPECIFIC_SUBTYPE;
353            }
354        }
355    
356        /**
357         * Gets the main type of the MIME type.
358         *
359         * @return the main type as a string.
360         */
361        public String getType()
362        {
363            return mimeType;
364        }
365    
366        /**
367         * Gets the subtype of the MIME type.
368         *
369         * @return the subtype as a string.
370         */
371        public String getSubtype()
372        {
373            return mimeSubtype;
374        }
375    
376        /**
377         * Gets the type and the subtype of the MIME type.
378         *
379         * @return the types as a string.
380         */
381        public String getTypes()
382        {
383            return mimeType + '/' + mimeSubtype;
384        }
385    
386        /**
387         * Checks whether the MIME type contains the specified parameter.
388         *
389         * @param param the name opf the parameter.
390         * @return true if the parameter found, otherwise false.
391         */
392        public boolean hasParameter(String param)
393        {
394            String[] na = parameterNames;
395            if (na != null)
396            {
397                for (int i = 0; i < na.length; i++)
398                {
399                    if (na[i].equalsIgnoreCase(param))
400                    {
401                        return true;
402                    }
403                }
404            }
405            return false;
406        }
407    
408        /**
409         * Gets the value of a MIME type parameter.
410         * The first parameter with the specifed name will be returned.
411         *
412         * @param param the name of the parameter.
413         * @return the value of the parameter, or null.
414         */
415        public String getParameter(String param)
416        {
417            String[] na = parameterNames;
418            if (na != null)
419            {
420                String[] va = parameterValues;
421                for (int i = 0; i < na.length; i++)
422                {
423                    if (na[i].equalsIgnoreCase(param))
424                    {
425                        return va[i];
426                    }
427                }
428            }
429            return null ;
430        }
431    
432        /**
433         * Sets the value of a MIME type parameter replacing the old one.
434         *
435         * @param param the name of the parameter.
436         * @param value the value of the parameter.
437         */
438        public synchronized void setParameter(String param,
439                                              String value)
440        {
441          if (parameterNames != null)
442          {
443              for (int i = 0; i < parameterNames.length; i++)
444              {
445                  if (parameterNames[i].equalsIgnoreCase(param))
446                  {
447                      parameterValues[i] = value;
448                      mimeTypeString = null;
449                      return;
450                  }
451              }
452          }
453          addParameter(param,value);
454        }
455    
456        /**
457         * Adds a parameter to the MIME type.
458         *
459         * @param param the name of the parameter.
460         * @param value the value of the parameter.
461         */
462        public void addParameter(String param,
463                                 String value)
464        {
465            addParameters(new String[]{ param },new String[]{ value });
466        }
467    
468        /**
469         * Adds parameters to the MIME type.
470         *
471         * @param params an array of parameter names.
472         * @param values an array of parameter values.
473         * @throws IllegalArgumentException for incorrect parameters.
474         */
475        public synchronized void addParameters(String[] params,
476                                               String[] values)
477        {
478            if ((params == null) ||
479                (values == null) ||
480                (params.length != values.length))
481                throw new IllegalArgumentException("Incorrect MIME type parameters");
482    
483            if (parameterNames != null)
484            {
485                String[] na = new String[parameterNames.length + params.length];
486                String[] va = new String[parameterValues.length + values.length];
487                System.arraycopy(parameterNames,0,na,0,parameterNames.length);
488                System.arraycopy(params,0,na,parameterNames.length,params.length);
489                System.arraycopy(parameterValues,0,va,0,parameterValues.length);
490                System.arraycopy(values,0,va,parameterValues.length,values.length);
491                parameterNames = na;
492                parameterValues = va;
493            }
494            else
495            {
496                parameterNames = params;
497                parameterValues = values;
498            }
499            mimeTypeString = null;
500        }
501    
502        /**
503         * Converts the MIME type into a string.
504         *
505         * @return the string representation of the MIME type.
506         */
507        public String toString()
508        {
509            if (mimeTypeString == null)
510            {
511                StringBuffer sb = new StringBuffer(mimeType);
512                sb.append('/');
513                sb.append(mimeSubtype);
514                String[] na = parameterNames;
515                if (na != null)
516                {
517                    String[] va = parameterValues;
518                    for (int i = 0; i < va.length; i++)
519                    {
520                        sb.append(';');
521                        sb.append(na[i]);
522                        if (va[i] != null)
523                        {
524                            sb.append('=');
525                            sb.append(va[i]);
526                        }
527                    }
528                }
529                mimeTypeString = sb.toString();
530            }
531            return mimeTypeString;
532        }
533    }