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.io.File;
025    import java.io.FileReader;
026    import java.io.StringReader;
027    import java.io.InputStream;
028    import java.io.InputStreamReader;
029    import java.io.BufferedReader;
030    import java.io.IOException;
031    import java.util.Map;
032    import java.util.HashMap;
033    import java.util.StringTokenizer;
034    
035    /**
036     * This class defines mappings between MIME types and the corresponding
037     * file name extensions. The mappings are defined as lines formed
038     * by a MIME type name followed by a list of extensions separated
039     * by a whitespace.
040     *
041     * @author <a href="mailto:ilkka.priha@simsoft.fi">Ilkka Priha</a>
042     * @author Daniel Rall
043     * @version $Id: MimeTypeMapper.java 826489 2009-10-18 18:54:59Z tv $
044     */
045    public class MimeTypeMapper
046    {
047        /**
048         * Mappings between MIME types and file name extensions.
049         */
050        private HashMap mimeTypeExtensions = new HashMap();
051        protected HashMap extensionMimeTypes = new HashMap();
052    
053        /**
054         * Constructs an empty MIME type mapper.
055         */
056        public MimeTypeMapper()
057        {
058            // do nothing
059        }
060    
061        /**
062         * Constructs a mapper reading from a stream.
063         *
064         * @param input an input stream.
065         * @throws IOException for an incorrect stream.
066         */
067        public MimeTypeMapper(InputStream input)
068            throws IOException
069        {
070            parse(new BufferedReader(
071                new InputStreamReader(input,CharSetMap.DEFAULT_CHARSET)));
072        }
073    
074        /**
075         * Constructs a mapper reading from a file.
076         *
077         * @param file an input file.
078         * @throws IOException for an incorrect file.
079         */
080        public MimeTypeMapper(File file)
081            throws IOException
082        {
083            FileReader freader = new FileReader(file);
084            try
085            {
086                parse(new BufferedReader(freader));
087            }
088            finally
089            {
090                try
091                {
092                    freader.close();
093                }
094                catch (IOException x)
095                {
096                    // ignore
097                }
098            }
099        }
100    
101        /**
102         * Constructs a mapper reading from a file path.
103         *
104         * @param path an input file path.
105         * @throws IOException for an incorrect file.
106         */
107        public MimeTypeMapper(String path)
108            throws IOException
109        {
110          this(new File(path));
111        }
112    
113        /**
114         * Sets a MIME content type mapping to extensions.
115         *
116         * @param spec a MIME type extension specification to parse.
117         */
118        public void setContentType(String spec)
119        {
120          try
121          {
122              parse(new BufferedReader(new StringReader(spec)));
123          }
124          catch (IOException x)
125          {
126              // ignore
127          }
128        }
129    
130        /**
131         * Gets a MIME content type corresponding to a specified file name
132         * extension.  If a mapping is initially not found, tries a second
133         * lookup using the provided extension in lower case.
134         *
135         * @param ext The file name extension to resolve.
136         * @return The MIME type, or <code>null</code> if not found.
137         */
138        public String getContentType(String ext)
139        {
140            String mimeType = (String) mimeTypeExtensions.get(ext);
141            if (mimeType == null && ext != null)
142            {
143                String lcExt = ext.toLowerCase();
144                if (!ext.equals(lcExt))
145                {
146                    // Original file extension didn't resolve, but was
147                    // mixed case.  Try it again with lower case chars.
148                    mimeType = (String) mimeTypeExtensions.get(lcExt);
149                }
150            }
151            return mimeType;
152        }
153    
154        /**
155         * Gets a file name extension corresponding to a specified MIME content type.
156         *
157         * @param type a MIME type as a string.
158         * @return the corresponding file name extension or null.
159         */
160        public String getExtension(String type)
161        {
162            return (String) extensionMimeTypes.get(type);
163        }
164    
165        /**
166         * Parses MIME type extensions.
167         *
168         * @param reader a reader to parse.
169         * @throws IOException for an incorrect reader.
170         */
171        protected synchronized void parse(BufferedReader reader)
172            throws IOException
173        {
174          int l,count = 0;
175          String next;
176          String str = null;
177          HashMap mimeTypes = (HashMap) extensionMimeTypes.clone();
178          HashMap extensions = (HashMap) mimeTypeExtensions.clone();
179          while ((next = reader.readLine()) != null)
180          {
181              str = str == null ? next : str + next;
182              if ((l = str.length()) == 0)
183              {
184                  str = null;
185                  continue;
186              }
187              // Check for continuation line.
188              if (str.charAt(l - 1) != '\\')
189              {
190                  count += parseMimeTypeExtension(str,mimeTypes,extensions);
191                  str = null;
192              }
193              else
194              {
195                  str = str.substring(0,l - 1);
196              }
197          }
198          if (str != null)
199          {
200              count += parseMimeTypeExtension(str,mimeTypes,extensions);
201          }
202          if (count > 0)
203          {
204              extensionMimeTypes = mimeTypes;
205              mimeTypeExtensions = extensions;
206          }
207        }
208    
209        /**
210         * Parses a MIME type extension.
211         *
212         * @param spec an extension specification to parse.
213         * @param mimeTypes a map of MIME types.
214         * @param extensions a map of extensions.
215         * @return the number of file name extensions parsed.
216         */
217        protected int parseMimeTypeExtension(String spec,
218                                             Map mimeTypes,
219                                             Map extensions)
220        {
221            int count = 0;
222            spec = spec.trim();
223            if ((spec.length() > 0) &&
224                (spec.charAt(0) != '#'))
225            {
226                StringTokenizer tokens = new StringTokenizer(spec);
227                String type = tokens.nextToken();
228                String ext;
229                while (tokens.hasMoreTokens())
230                {
231                    ext = tokens.nextToken();
232                    if (ext.length() == 0)
233                    {
234                        continue;
235                    }
236                    extensions.put(ext,type);
237                    if (count++ == 0)
238                    {
239                        mimeTypes.put(type,ext);
240                    }
241                }
242            }
243            return count;
244        }
245    }