/* * eXist Open Source Native XML Database * Copyright (C) 2001-04 The eXist Project * http://exist-db.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Id$ */ package org.exist.util; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.util.Iterator; import java.util.Map; import java.util.StringTokenizer; import java.util.TreeMap; import java.util.Vector; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import org.exist.xmldb.XmldbURI; import org.xml.sax.Attributes; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.XMLReader; import org.xml.sax.helpers.DefaultHandler; /** * Global table of mime types. This singleton class maintains a list * of mime types known to the system. It is used to look up the * mime type for a specific file extension and to check if a file * is an XML or binary resource. * * The mime type table is read from a file "mime-types.xml", * which should reside in the directory identified in the exist home * directory. If no such file is found, the class tries * to load the default map from the org.exist.util package via the * class loader. * * @author wolf */ public class MimeTable { private static final String FILE_LOAD_FAILED_ERR = "Failed to load mime-type table from "; private static final String LOAD_FAILED_ERR = "Failed to load mime-type table from class loader"; private static final String MIME_TYPES_XML = "mime-types.xml"; private static final String MIME_TYPES_XML_DEFAULT = "org/exist/util/" + MIME_TYPES_XML; private static MimeTable instance = null; /** From where the mime table is loeaded for message purpose */ private String src; /** * Returns the singleton. */ public static MimeTable getInstance() { if (instance == null) instance = new MimeTable(); return instance; } /** * Returns the singleton, using a custom mime-types.xml file */ public static MimeTable getInstance(File f) { if (instance == null) instance = new MimeTable(f); return instance; } /** * Returns the singleton, using a custom mime-types.xml stream, * like for instance an internal database resource. */ public static MimeTable getInstance(InputStream stream,String src) { if (instance == null) instance = new MimeTable(stream,src); return instance; } private Map mimeTypes = new TreeMap(); private Map extensions = new TreeMap(); private Map preferredExtension = new TreeMap(); public MimeTable() { load(); } public MimeTable(File f) { load(f); } public MimeTable(InputStream stream,String src) { load(stream,src); } /** * Inform from where a mime-table is loaded */ public String getSrc() { return this.src; } //TODO: deprecate? public MimeType getContentTypeFor(String fileName) { String ext = getExtension(fileName); return ext == null ? null : (MimeType) extensions.get(ext); } public MimeType getContentTypeFor(XmldbURI fileName) { return getContentTypeFor(fileName.toString()); } public MimeType getContentType(String mimeType) { return (MimeType) mimeTypes.get(mimeType); } public Vector getAllExtensions(MimeType mimeType) { return getAllExtensions(mimeType.getName()); } public Vector getAllExtensions(String mimeType) { Vector extns = new Vector(); for(Iterator itExtensions = extensions.keySet().iterator(); itExtensions.hasNext();) { String extKey = (String)itExtensions.next(); MimeType mt = (MimeType)extensions.get(extKey); if(mt.getName().equals(mimeType)) { extns.add(extKey); } } String preferred = (String)preferredExtension.get(mimeType); if(preferred != null && !extns.contains(preferred)) { extns.add(0, preferred); } return extns; } public String getPreferredExtension(MimeType mimeType) { return getPreferredExtension(mimeType.getName()); } public String getPreferredExtension(String mimeType) { return (String)preferredExtension.get(mimeType); } public boolean isXMLContent(String fileName) { String ext = getExtension(fileName); if (ext == null) return false; MimeType type = (MimeType) extensions.get(ext); if (type == null) return false; return type.getType() == MimeType.XML; } private String getExtension(String fileName) { File f = new File(fileName); fileName = f.getName(); int p = fileName.lastIndexOf('.'); if (p < 0 || p + 1 == fileName.length()) return null; return fileName.substring(p).toLowerCase(); } private void load() { load(ConfigurationHelper.lookup(MIME_TYPES_XML)); } private void load(InputStream stream,String src) { boolean loaded = false; System.out.println("Loading mime table from stream "+src); try { loadMimeTypes(stream); this.src=src; } catch (ParserConfigurationException e) { System.err.println(LOAD_FAILED_ERR); } catch (SAXException e) { System.err.println(LOAD_FAILED_ERR); } catch (IOException e) { System.err.println(LOAD_FAILED_ERR); } if (!loaded) { ClassLoader cl = MimeTable.class.getClassLoader(); InputStream is = cl.getResourceAsStream(MIME_TYPES_XML_DEFAULT); if (is == null) { System.err.println(LOAD_FAILED_ERR); } try { loadMimeTypes(is); this.src="resource://"+MIME_TYPES_XML_DEFAULT; } catch (ParserConfigurationException e) { System.err.println(LOAD_FAILED_ERR); } catch (SAXException e) { System.err.println(LOAD_FAILED_ERR); } catch (IOException e) { System.err.println(LOAD_FAILED_ERR); } } } private void load(File f) { boolean loaded = false; if (f.canRead()) { try { System.out.println("Loading mime table from file " + f.getAbsolutePath()); loadMimeTypes(new FileInputStream(f)); loaded = true; this.src=f.toURI().toString(); } catch (FileNotFoundException e) { System.err.println(FILE_LOAD_FAILED_ERR + f.getAbsolutePath()); } catch (ParserConfigurationException e) { System.err.println(FILE_LOAD_FAILED_ERR + f.getAbsolutePath()); } catch (SAXException e) { System.err.println(FILE_LOAD_FAILED_ERR + f.getAbsolutePath()); } catch (IOException e) { System.err.println(FILE_LOAD_FAILED_ERR + f.getAbsolutePath()); } } if (!loaded) { ClassLoader cl = MimeTable.class.getClassLoader(); InputStream is = cl.getResourceAsStream(MIME_TYPES_XML_DEFAULT); if (is == null) { System.err.println(LOAD_FAILED_ERR); } try { loadMimeTypes(is); this.src="resource://"+MIME_TYPES_XML_DEFAULT; } catch (ParserConfigurationException e) { System.err.println(LOAD_FAILED_ERR); } catch (SAXException e) { System.err.println(LOAD_FAILED_ERR); } catch (IOException e) { System.err.println(LOAD_FAILED_ERR); } } } /** * @param stream * @throws SAXException * @throws ParserConfigurationException * @throws IOException */ private void loadMimeTypes(InputStream stream) throws ParserConfigurationException, SAXException, IOException { SAXParserFactory factory = SAXParserFactory.newInstance(); factory.setNamespaceAware(true); factory.setValidating(false); InputSource src = new InputSource(stream); SAXParser parser = factory.newSAXParser(); XMLReader reader = parser.getXMLReader(); reader.setContentHandler(new MimeTableHandler()); reader.parse(new InputSource(stream)); } private class MimeTableHandler extends DefaultHandler { private static final String EXTENSIONS = "extensions"; private static final String DESCRIPTION = "description"; private static final String MIME_TYPE = "mime-type"; private MimeType mime = null; private FastStringBuffer charBuf = new FastStringBuffer(64); /* (non-Javadoc) * @see org.xml.sax.helpers.DefaultHandler#startElement(java.lang.String, java.lang.String, java.lang.String, org.xml.sax.Attributes) */ public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { if (MIME_TYPE.equals(qName)) { String name = attributes.getValue("name"); if (name == null || name.length() == 0) { System.err.println("No name specified for mime-type"); return; } int type = MimeType.BINARY; String typeAttr = attributes.getValue("type"); if (typeAttr != null && "xml".equals(typeAttr)) type = MimeType.XML; mime = new MimeType(name, type); mimeTypes.put(name, mime); } charBuf.setLength(0); } /* (non-Javadoc) * @see org.xml.sax.helpers.DefaultHandler#endElement(java.lang.String, java.lang.String, java.lang.String) */ public void endElement(String uri, String localName, String qName) throws SAXException { if (MIME_TYPE.equals(qName)) { mime = null; } else if (DESCRIPTION.equals(qName)) { if (mime != null) { String description = charBuf.getNormalizedString(FastStringBuffer.SUPPRESS_BOTH); mime.setDescription(description); } } else if (EXTENSIONS.equals(qName)) { if (mime != null) { String extList = charBuf.getNormalizedString(FastStringBuffer.SUPPRESS_BOTH); StringTokenizer tok = new StringTokenizer(extList, ", "); String preferred = null; while (tok.hasMoreTokens()) { String ext = tok.nextToken().toLowerCase(); if (!extensions.containsKey(ext)) { extensions.put(ext, mime); } if (preferred==null) { preferred = ext; } } preferredExtension.put(mime.getName(),preferred); } } } /* (non-Javadoc) * @see org.xml.sax.helpers.DefaultHandler#characters(char[], int, int) */ public void characters(char[] ch, int start, int length) throws SAXException { charBuf.append(ch, start, length); } } public static void main(String[] args) { MimeTable table = MimeTable.getInstance(); MimeType type = table.getContentTypeFor("samples/xquery/fibo.svg"); if (type == null) { System.out.println("Not found!"); } else { System.out.println(type.getName()); System.out.println(type.getDescription()); } } }