View Javadoc

1   /**
2    *
3    * Copyright 2003-2004 The Apache Software Foundation
4    *
5    *  Licensed under the Apache License, Version 2.0 (the "License");
6    *  you may not use this file except in compliance with the License.
7    *  You may obtain a copy of the License at
8    *
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   *
11   *  Unless required by applicable law or agreed to in writing, software
12   *  distributed under the License is distributed on an "AS IS" BASIS,
13   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   *  See the License for the specific language governing permissions and
15   *  limitations under the License.
16   */
17  
18  package javax.activation;
19  
20  import java.io.BufferedReader;
21  import java.io.File;
22  import java.io.FileInputStream;
23  import java.io.FileReader;
24  import java.io.IOException;
25  import java.io.InputStream;
26  import java.io.InputStreamReader;
27  import java.io.Reader;
28  import java.net.URL;
29  import java.security.Security;
30  import java.util.ArrayList;
31  import java.util.Collections;
32  import java.util.Enumeration;
33  import java.util.HashMap;
34  import java.util.Iterator;
35  import java.util.List;
36  import java.util.Map;
37  
38  /**
39   * @version $Rev: 376811 $ $Date: 2006-02-10 11:40:13 -0800 (Fri, 10 Feb 2006) $
40   */
41  public class MailcapCommandMap extends CommandMap {
42      private final Map preferredCommands = new HashMap();
43      private final Map allCommands = new HashMap();
44      // commands identified as fallbacks...these are used last, and also used as wildcards.
45      private final Map fallbackCommands = new HashMap();
46      private URL url;
47  
48      public MailcapCommandMap() {
49          ClassLoader contextLoader = Thread.currentThread().getContextClassLoader();
50          // process /META-INF/mailcap.default
51          try {
52              InputStream is = MailcapCommandMap.class.getResourceAsStream("/META-INF/mailcap.default");
53              if (is != null) {
54                  try {
55                      parseMailcap(is);
56                  } finally {
57                      is.close();
58                  }
59              }
60          } catch (IOException e) {
61              // ignore
62          }
63  
64          // process /META-INF/mailcap resources
65          try {
66              Enumeration e = contextLoader.getResources("META-INF/mailcap");
67              while (e.hasMoreElements()) {
68                  url = ((URL) e.nextElement());
69                  try {
70                      InputStream is = url.openStream();
71                      try {
72                          parseMailcap(is);
73                      } finally {
74                          is.close();
75                      }
76                  } catch (IOException e1) {
77                      continue;
78                  }
79              }
80          } catch (SecurityException e) {
81              // ignore
82          } catch (IOException e) {
83              // ignore
84          }
85  
86          // process ${java.home}/lib/mailcap
87          try {
88              File file = new File(System.getProperty("java.home"), "lib/mailcap");
89              InputStream is = new FileInputStream(file);
90              try {
91                  parseMailcap(is);
92              } finally {
93                  is.close();
94              }
95          } catch (SecurityException e) {
96              // ignore
97          } catch (IOException e) {
98              // ignore
99          }
100 
101         // process ${user.home}/lib/mailcap
102         try {
103             File file = new File(System.getProperty("user.home"), ".mailcap");
104             InputStream is = new FileInputStream(file);
105             try {
106                 parseMailcap(is);
107             } finally {
108                 is.close();
109             }
110         } catch (SecurityException e) {
111             // ignore
112         } catch (IOException e) {
113             // ignore
114         }
115     }
116 
117     public MailcapCommandMap(String fileName) throws IOException {
118         this();
119         FileReader reader = new FileReader(fileName);
120         try {
121             parseMailcap(reader);
122         } finally {
123             reader.close();
124         }
125     }
126 
127     public MailcapCommandMap(InputStream is) {
128         this();
129         parseMailcap(is);
130     }
131 
132     private void parseMailcap(InputStream is) {
133         try {
134             parseMailcap(new InputStreamReader(is));
135         } catch (IOException e) {
136             // spec API means all we can do is swallow this
137         }
138     }
139 
140     void parseMailcap(Reader reader) throws IOException {
141         BufferedReader br = new BufferedReader(reader);
142         String line;
143         while ((line = br.readLine()) != null) {
144             addMailcap(line);
145         }
146     }
147 
148     public synchronized void addMailcap(String mail_cap) {
149         int index = 0;
150         // skip leading whitespace
151         index = skipSpace(mail_cap, index);
152         if (index == mail_cap.length() || mail_cap.charAt(index) == '#') {
153             return;
154         }
155 
156         // get primary type
157         int start = index;
158         index = getToken(mail_cap, index);
159         if (start == index) {
160             return;
161         }
162         String mimeType = mail_cap.substring(start, index);
163 
164         // skip any spaces after the primary type
165         index = skipSpace(mail_cap, index);
166         if (index == mail_cap.length() || mail_cap.charAt(index) == '#') {
167             return;
168         }
169 
170         // get sub-type
171         if (mail_cap.charAt(index) == '/') {
172             index = skipSpace(mail_cap, ++index);
173             start = index;
174             index = getToken(mail_cap, index);
175             mimeType = mimeType + '/' + mail_cap.substring(start, index);
176         } else {
177 
178             mimeType = mimeType + "/*";
179         }
180 
181         // we record all mappings using the lowercase version.
182         mimeType = mimeType.toLowerCase();
183 
184         // skip spaces after mime type
185         index = skipSpace(mail_cap, index);
186 
187         // expect a ';' to terminate field 1
188         if (index == mail_cap.length() || mail_cap.charAt(index) != ';') {
189             return;
190         }
191         index = getMText(mail_cap, index);
192         // expect a ';' to terminate field 2
193         if (index == mail_cap.length() || mail_cap.charAt(index) != ';') {
194             return;
195         }
196 
197         // we don't know which list this will be added to until we finish parsing, as there
198         // can be an x-java-fallback-entry parameter that moves this to the fallback list.
199         List commandList = new ArrayList();
200         // but by default, this is not a fallback.
201         boolean fallback = false;
202 
203         // parse fields
204         while (index < mail_cap.length() && mail_cap.charAt(index) == ';') {
205             index = skipSpace(mail_cap, index + 1);
206             start = index;
207             index = getToken(mail_cap, index);
208             String fieldName = mail_cap.substring(start, index).toLowerCase();
209             index = skipSpace(mail_cap, index);
210             if (index < mail_cap.length() && mail_cap.charAt(index) == '=') {
211                 index = skipSpace(mail_cap, index + 1);
212                 start = index;
213                 index = getMText(mail_cap, index);
214                 String value = mail_cap.substring(start, index);
215                 index = skipSpace(mail_cap, index);
216                 if (fieldName.startsWith("x-java-") && fieldName.length() > 7) {
217                     String command = fieldName.substring(7);
218                     value = value.trim();
219                     if (command.equals("fallback-entry")) {
220                         if (value.equals("true")) {
221                             fallback = true;
222                         }
223                     }
224                     else {
225                         // create a CommandInfo item and add it the accumulator
226                         CommandInfo info = new CommandInfo(command, value);
227                         commandList.add(info);
228                     }
229                 }
230             }
231         }
232         addCommands(mimeType, commandList, fallback);
233     }
234 
235     /**
236      * Add a parsed list of commands to the appropriate command list.
237      *
238      * @param mimeType The mimeType name this is added under.
239      * @param commands A List containing the command information.
240      * @param fallback The target list identifier.
241      */
242     private void addCommands(String mimeType, List commands, boolean fallback) {
243         // the target list changes based on the type of entry.
244         Map target = fallback ? fallbackCommands : preferredCommands;
245 
246         // now process
247         for (Iterator i = commands.iterator(); i.hasNext();) {
248             CommandInfo info = (CommandInfo)i.next();
249             addCommand(target, mimeType, info);
250             // if this is not a fallback position, then this to the allcommands list.
251             if (!fallback) {
252                 List cmdList = (List) allCommands.get(mimeType);
253                 if (cmdList == null) {
254                     cmdList = new ArrayList();
255                     allCommands.put(mimeType, cmdList);
256                 }
257                 cmdList.add(info);
258             }
259         }
260     }
261 
262 
263     /**
264      * Add a command to a target command list (preferred or fallback).
265      *
266      * @param commandList
267      *                 The target command list.
268      * @param mimeType The MIME type the command is associated with.
269      * @param command  The command information.
270      */
271     private void addCommand(Map commandList, String mimeType, CommandInfo command) {
272 
273         Map commands = (Map) commandList.get(mimeType);
274         if (commands == null) {
275             commands = new HashMap();
276             commandList.put(mimeType, commands);
277         }
278         commands.put(command.getCommandName(), command);
279     }
280 
281 
282     private int skipSpace(String s, int index) {
283         while (index < s.length() && Character.isWhitespace(s.charAt(index))) {
284             index++;
285         }
286         return index;
287     }
288 
289     private int getToken(String s, int index) {
290         while (index < s.length() && s.charAt(index) != '#' && !MimeType.isSpecial(s.charAt(index))) {
291             index++;
292         }
293         return index;
294     }
295 
296     private int getMText(String s, int index) {
297         while (index < s.length()) {
298             char c = s.charAt(index);
299             if (c == '#' || c == ';' || Character.isISOControl(c)) {
300                 return index;
301             }
302             if (c == '\\') {
303                 index++;
304                 if (index == s.length()) {
305                     return index;
306                 }
307             }
308             index++;
309         }
310         return index;
311     }
312 
313     public synchronized CommandInfo[] getPreferredCommands(String mimeType) {
314         // get the mimetype as a lowercase version.
315         mimeType = mimeType.toLowerCase();
316 
317         Map commands = (Map) preferredCommands.get(mimeType);
318         if (commands == null) {
319             commands = (Map) preferredCommands.get(getWildcardMimeType(mimeType));
320         }
321 
322         Map fallbackCommands = getFallbackCommands(mimeType);
323 
324         // if we have fall backs, then we need to merge this stuff.
325         if (fallbackCommands != null) {
326             // if there's no command list, we can just use this as the master list.
327             if (commands == null) {
328                 commands = fallbackCommands;
329             }
330             else {
331                 // merge the two lists.  The ones in the commands list will take precedence.
332                 commands = mergeCommandMaps(commands, fallbackCommands);
333             }
334         }
335 
336         // now convert this into an array result.
337         if (commands == null) {
338             return new CommandInfo[0];
339         }
340         return (CommandInfo[]) commands.values().toArray(new CommandInfo[commands.size()]);
341     }
342 
343     private Map getFallbackCommands(String mimeType) {
344         Map commands = (Map) fallbackCommands.get(mimeType);
345 
346         // now we also need to search this as if it was a wildcard.  If we get a wildcard hit,
347         // we have to merge the two lists.
348         Map wildcardCommands = (Map)fallbackCommands.get(getWildcardMimeType(mimeType));
349         // no wildcard version
350         if (wildcardCommands == null) {
351             return commands;
352         }
353         // we need to merge these.
354         return mergeCommandMaps(commands, wildcardCommands);
355     }
356 
357 
358     private Map mergeCommandMaps(Map main, Map fallback) {
359         // create a cloned copy of the second map.  We're going to use a PutAll operation to
360         // overwrite any duplicates.
361         Map result = new HashMap(fallback);
362         result.putAll(main);
363 
364         return result;
365     }
366 
367     public synchronized CommandInfo[] getAllCommands(String mimeType) {
368         mimeType = mimeType.toLowerCase();
369         List exactCommands = (List) allCommands.get(mimeType);
370         if (exactCommands == null) {
371             exactCommands = Collections.EMPTY_LIST;
372         }
373         List wildCommands = (List) allCommands.get(getWildcardMimeType(mimeType));
374         if (wildCommands == null) {
375             wildCommands = Collections.EMPTY_LIST;
376         }
377 
378         Map fallbackCommands = getFallbackCommands(mimeType);
379         if (fallbackCommands == null) {
380             fallbackCommands = Collections.EMPTY_MAP;
381         }
382 
383 
384         CommandInfo[] result = new CommandInfo[exactCommands.size() + wildCommands.size() + fallbackCommands.size()];
385         int j = 0;
386         for (int i = 0; i < exactCommands.size(); i++) {
387             result[j++] = (CommandInfo) exactCommands.get(i);
388         }
389         for (int i = 0; i < wildCommands.size(); i++) {
390             result[j++] = (CommandInfo) wildCommands.get(i);
391         }
392 
393         for (Iterator i = fallbackCommands.keySet().iterator(); i.hasNext();) {
394             result[j++] = (CommandInfo) fallbackCommands.get((String)i.next());
395         }
396         return result;
397     }
398 
399     public synchronized CommandInfo getCommand(String mimeType, String cmdName) {
400         mimeType = mimeType.toLowerCase();
401         // strip any parameters from the supplied mimeType
402         int i = mimeType.indexOf(';');
403         if (i != -1) {
404             mimeType = mimeType.substring(0, i).trim();
405         }
406 
407         // search for an exact match
408         Map commands = (Map) preferredCommands.get(mimeType);
409         if (commands == null) {
410             // then a wild card match
411             commands = (Map) preferredCommands.get(getWildcardMimeType(mimeType));
412             if (commands == null) {
413                 // then fallback searches, both standard and wild card.
414                 commands = (Map) fallbackCommands.get(mimeType);
415                 if (commands == null) {
416                     commands = (Map) fallbackCommands.get(getWildcardMimeType(mimeType));
417                 }
418                 if (commands == null) {
419                     return null;
420                 }
421             }
422         }
423         return (CommandInfo) commands.get(cmdName.toLowerCase());
424     }
425 
426     private String getWildcardMimeType(String mimeType) {
427         int i = mimeType.indexOf('/');
428         if (i == -1) {
429             return mimeType + "/*";
430         } else {
431             return mimeType.substring(0, i + 1) + "*";
432         }
433     }
434 
435     public synchronized DataContentHandler createDataContentHandler(String mimeType) {
436 
437         CommandInfo info = getCommand(mimeType, "content-handler");
438         if (info == null) {
439             return null;
440         }
441 
442         ClassLoader cl = Thread.currentThread().getContextClassLoader();
443         if (cl == null) {
444             cl = getClass().getClassLoader();
445         }
446         try {
447             return (DataContentHandler) cl.loadClass(info.getCommandClass()).newInstance();
448         } catch (ClassNotFoundException e) {
449             return null;
450         } catch (IllegalAccessException e) {
451             return null;
452         } catch (InstantiationException e) {
453             return null;
454         }
455     }
456 }