[Mulgara-svn] r2010 - in trunk/src/jar: content-mbox/java/org/mulgara/content/mbox content-mbox/java/org/mulgara/content/mbox/parser/model/exception content-mp3/java/org/mulgara/content/mp3 content-n3/java/org/mulgara/content/n3 content-rdfa/java/org/mulgara/content/rdfa content-rdfxml/java/org/mulgara/content/rdfxml content-rdfxml/java/org/mulgara/content/rdfxml/writer content-rlog/java/org/mulgara/content/rlog query/java/org/mulgara/query/rdf resolver/java/org/mulgara/resolver resolver-spi/java/org/mulgara/content

alexhall at mulgara.org alexhall at mulgara.org
Tue Jul 19 20:28:15 UTC 2011


Author: alexhall
Date: 2011-07-19 20:28:14 +0000 (Tue, 19 Jul 2011)
New Revision: 2010

Added:
   trunk/src/jar/query/java/org/mulgara/query/rdf/MimeTypes.java
   trunk/src/jar/resolver-spi/java/org/mulgara/content/ValidatingContentHandler.java
Modified:
   trunk/src/jar/content-mbox/java/org/mulgara/content/mbox/MBoxContentHandler.java
   trunk/src/jar/content-mbox/java/org/mulgara/content/mbox/parser/model/exception/InvalidMBoxException.java
   trunk/src/jar/content-mp3/java/org/mulgara/content/mp3/MP3ContentHandler.java
   trunk/src/jar/content-n3/java/org/mulgara/content/n3/N3ContentHandler.java
   trunk/src/jar/content-n3/java/org/mulgara/content/n3/Parser.java
   trunk/src/jar/content-rdfa/java/org/mulgara/content/rdfa/RdfaContentHandler.java
   trunk/src/jar/content-rdfa/java/org/mulgara/content/rdfa/RdfaStatementsUnitTest.java
   trunk/src/jar/content-rdfa/java/org/mulgara/content/rdfa/StatementParser.java
   trunk/src/jar/content-rdfxml/java/org/mulgara/content/rdfxml/RDFXMLContentHandler.java
   trunk/src/jar/content-rdfxml/java/org/mulgara/content/rdfxml/writer/RDFXMLWriterUnitTest.java
   trunk/src/jar/content-rlog/java/org/mulgara/content/rlog/RlogContentHandler.java
   trunk/src/jar/resolver-spi/java/org/mulgara/content/ContentHandler.java
   trunk/src/jar/resolver/java/org/mulgara/resolver/BasicDatabaseSessionUnitTest.java
   trunk/src/jar/resolver/java/org/mulgara/resolver/ContentHandlerManagerImpl.java
   trunk/src/jar/resolver/java/org/mulgara/resolver/SetGraphOperation.java
   trunk/src/jar/resolver/java/org/mulgara/resolver/StreamContent.java
Log:
Invert the logic for selecting content handlers by eliminating the ContentHandler.canParse method and making content handlers describe themselves in terms of their supported MIME types and filename extensions, and letting the content handler manager control the selection. This reduces the risk of a misbehaved content handler claiming to support content that it doesn't. Also, modified the StreamContent constructor to allow us to specify both a MIME type and a base URI, which allows us to prefer the MIME type when both are present.

Modified: trunk/src/jar/content-mbox/java/org/mulgara/content/mbox/MBoxContentHandler.java
===================================================================
--- trunk/src/jar/content-mbox/java/org/mulgara/content/mbox/MBoxContentHandler.java	2011-07-19 18:22:41 UTC (rev 2009)
+++ trunk/src/jar/content-mbox/java/org/mulgara/content/mbox/MBoxContentHandler.java	2011-07-19 20:28:14 UTC (rev 2010)
@@ -28,17 +28,20 @@
 package org.mulgara.content.mbox;
 
 // Java 2 standard packages
-import java.io.*;
-import java.net.URI;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.Collection;
+import java.util.Collections;
 
-// Third party packages
-import org.apache.log4j.Logger; // Apache Log4J
+import javax.activation.MimeType;
 
-// Local packages
+import org.apache.log4j.Logger;
 import org.mulgara.content.Content;
-import org.mulgara.content.ContentHandler;
 import org.mulgara.content.ContentHandlerException;
 import org.mulgara.content.NotModifiedException;
+import org.mulgara.content.ValidatingContentHandler;
 import org.mulgara.content.mbox.parser.model.exception.InvalidMBoxException;
 import org.mulgara.query.TuplesException;
 import org.mulgara.resolver.spi.ResolverSession;
@@ -62,12 +65,22 @@
  *
  * @licence <a href="{@docRoot}/../../LICENCE">Mozilla Public License v1.1</a>
  */
-public class MBoxContentHandler implements ContentHandler {
+public class MBoxContentHandler implements ValidatingContentHandler {
 
   /** Logger. */
+  @SuppressWarnings("unused")
   private static Logger log = Logger.getLogger(MBoxContentHandler.class);
 
+  /** Don't know our MIME type. */
+  public Collection<MimeType> getContentTypes() {
+    return Collections.emptyList();
+  }
 
+  /** Don't know our filename extensions. */
+  public Collection<String> getFileExtensions() {
+    return Collections.emptyList();
+  }
+
   /**
    * Parses the messages of an mbox file pointed to by the content object which
    * are then converted to a statements object.
@@ -100,52 +113,6 @@
   }
 
   /**
-   * @return <code>true</code> if the file part of the URI has a valid mbox
-   *         format (ie The file starts with "from ")
-   */
-  public boolean canParse(Content content) throws NotModifiedException {
-
-    if (content.isStreaming()) {
-      log.info("Unable to parse streaming content in mbox content handler.");
-      return false;
-    }
-
-
-    // Ignoring the MIME type
-
-    // Container for our input stream from the mbox file
-    InputStream inputStream = null;
-
-    try {
-
-      // Obtain the input stream to our file
-      inputStream = content.newInputStream();
-    } catch (NotModifiedException e) {
-      // Not only CAN we parse this, we already have
-      return true;
-    } catch (IOException ioException) {
-
-      // If we can't obtain an input stream, chances are our mbox file has
-      // problems and is most likely invalid and non-parsable
-      return false;
-    }
-
-    try {
-
-      // Attempt to validate the inputStream to the file in order to validate
-      // that we are working with an MBox
-      validate(inputStream, content.getURI());
-    } catch (InvalidMBoxException invalidMBoxException) {
-
-      // If the mbox is in an invalid format then we can't use it so state
-      // that we can't parse it
-      return false;
-    }
-
-    return true;
-  }
-
-  /**
    * @throws ContentHandlerException  always, as mboxes aren't pure metadata
    *   stores and don't make any sense to overwrite
    */
@@ -169,15 +136,20 @@
    *
    * @throws InvalidMBoxException
    */
-  private void validate(InputStream stream, URI uri) throws InvalidMBoxException {
-
+  public boolean validate(Content content) throws NotModifiedException {
+    if (content == null) return false;
+    
+    InputStream stream;
+    try {
+      stream = content.newInputStream();
+    } catch (IOException e) {
+      return false;
+    }
     if (stream == null) {
       // The mbox file cannot be null
-      throw new InvalidMBoxException("Cannot parse null mbox objects.");
+      return false;
     }
 
-    String file = (uri == null) ? "<<stream>>" : uri.toString();
-
     // Create an input stream reader
     InputStreamReader inputReader = new InputStreamReader(stream);
 
@@ -195,18 +167,11 @@
         line = reader.readLine();
       }
     } catch (IOException ioException) {
-
-      // We could not read the mbox file
-      throw new InvalidMBoxException("MBox file [" + file +
-                                     "] was not able to be read from.",
-                                     ioException);
+      return false;
     }
 
     if (!line.toLowerCase().startsWith("from ")) {
-
-      // The mbox is not RFC822 compliant
-      throw new InvalidMBoxException("MBox file [" + file +
-                                     "] was not a valid RFC822 mbox.");
+      return false;
     } else {
 
       try {
@@ -214,18 +179,13 @@
         // Get the next line of text (if any)
         line = reader.readLine();
       } catch (IOException ioException) {
-
-        // We could not read the mbox file
-        throw new InvalidMBoxException("MBox file [" + file +
-                                       "] was not able to be read from.",
-                                       ioException);
+        return false;
       }
       if (line != null && line.length() > 0 &&
           !line.split(" ")[0].endsWith(":")) {
 
         // The mbox is not RFC822 compliant if the next line is not a header
-      throw new InvalidMBoxException("MBox file [" + file +
-                                     "] was not a valid RFC822 mbox.");
+        return false;
       }
     }
 
@@ -233,9 +193,9 @@
 
       reader.close();
     } catch (IOException ioException) {
-
-      // Since we are validating and there is not much we can do about an
-      // unclosable file reader we just ignore it
+      return false;
     }
+    
+    return true;
   }
 }

Modified: trunk/src/jar/content-mbox/java/org/mulgara/content/mbox/parser/model/exception/InvalidMBoxException.java
===================================================================
--- trunk/src/jar/content-mbox/java/org/mulgara/content/mbox/parser/model/exception/InvalidMBoxException.java	2011-07-19 18:22:41 UTC (rev 2009)
+++ trunk/src/jar/content-mbox/java/org/mulgara/content/mbox/parser/model/exception/InvalidMBoxException.java	2011-07-19 20:28:14 UTC (rev 2010)
@@ -27,6 +27,8 @@
 
 package org.mulgara.content.mbox.parser.model.exception;
 
+import org.mulgara.content.ContentHandlerException;
+
 /**
  * An exception that should be used when an MBox model object tries to parse an
  * invalid mbox.
@@ -48,7 +50,7 @@
  *
  * @licence <a href="{@docRoot}/../../LICENCE">Mozilla Public License v1.1</a>
  */
-public class InvalidMBoxException extends Exception {
+public class InvalidMBoxException extends ContentHandlerException {
 
   /** Generated serialization ID. */
   private static final long serialVersionUID = 5609261818493877883L;

Modified: trunk/src/jar/content-mp3/java/org/mulgara/content/mp3/MP3ContentHandler.java
===================================================================
--- trunk/src/jar/content-mp3/java/org/mulgara/content/mp3/MP3ContentHandler.java	2011-07-19 18:22:41 UTC (rev 2009)
+++ trunk/src/jar/content-mp3/java/org/mulgara/content/mp3/MP3ContentHandler.java	2011-07-19 20:28:14 UTC (rev 2010)
@@ -30,19 +30,19 @@
 // Java 2 standard packages
 // Java 2 enterprise packages
 import java.net.URI;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
 
 import javax.activation.MimeType;
-import javax.activation.MimeTypeParseException;
 
-// Third party packages
-import org.apache.log4j.Logger; // Apache Log4J
-
-// Local packages
+import org.apache.log4j.Logger;
 import org.mulgara.content.Content;
 import org.mulgara.content.ContentHandler;
 import org.mulgara.content.ContentHandlerException;
 import org.mulgara.content.NotModifiedException;
 import org.mulgara.query.TuplesException;
+import org.mulgara.query.rdf.MimeTypes;
 import org.mulgara.resolver.spi.ResolverSession;
 import org.mulgara.resolver.spi.Statements;
 
@@ -70,17 +70,26 @@
   @SuppressWarnings("unused")
   private static Logger logger = Logger.getLogger(MP3ContentHandler.class.getName());
 
-  /**
-   * The MIME type of RDF/XML.
+  /** Supported content types */
+  private static final Collection<MimeType> mimeTypes = 
+    Collections.unmodifiableList(Arrays.asList(MimeTypes.AUDIO_MPEG));
+
+  /** Supported file types */
+  private static final Collection<String> fileExts =
+    Collections.unmodifiableList(Arrays.asList("mp3"));
+
+  /* (non-Javadoc)
+   * @see org.mulgara.content.ContentHandler#getContentTypes()
    */
-  private static final MimeType AUDIO_MPEG;
+  public Collection<MimeType> getContentTypes() {
+    return mimeTypes;
+  }
 
-  static {
-    try {
-      AUDIO_MPEG = new MimeType("audio", "mpeg");
-    } catch (MimeTypeParseException e) {
-      throw new ExceptionInInitializerError(e);
-    }
+  /* (non-Javadoc)
+   * @see org.mulgara.content.ContentHandler#getFileExtensions()
+   */
+  public Collection<String> getFileExtensions() {
+    return fileExts;
   }
 
   /**
@@ -118,32 +127,6 @@
   }
 
   /**
-   * @return <code>true</code> if the file part of the URI has an
-   *   <code>.mp3</code> extension
-   */
-  public boolean canParse(Content content) throws NotModifiedException
-  {
-    MimeType contentType = content.getContentType();
-    if (contentType != null && AUDIO_MPEG.match(contentType)) {
-      return true;
-    }
-
-    if (content.getURI() == null) {
-      return false;
-    }
-                                                                                
-    // Obtain the path part of the URI
-    String path = content.getURI().getPath();
-    if (path == null) {
-      return false;
-    }
-    assert path != null;
-                                                                                
-    // We recognize a fixed extension
-    return path.endsWith(".mp3");
-  }
-
-  /**
    * @throws ContentHandlerException  always, as MP3s aren't pure metadata
    *   stores and don't make any sense to overwrite
    */

Modified: trunk/src/jar/content-n3/java/org/mulgara/content/n3/N3ContentHandler.java
===================================================================
--- trunk/src/jar/content-n3/java/org/mulgara/content/n3/N3ContentHandler.java	2011-07-19 18:22:41 UTC (rev 2009)
+++ trunk/src/jar/content-n3/java/org/mulgara/content/n3/N3ContentHandler.java	2011-07-19 20:28:14 UTC (rev 2010)
@@ -32,20 +32,18 @@
 import java.io.IOException;
 import java.io.OutputStreamWriter;
 import java.io.Writer;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
 
-// Java 2 enterprise packages
 import javax.activation.MimeType;
-import javax.activation.MimeTypeParseException;
 
-// Third party packages
-import org.apache.log4j.Logger;  // Apache Log4J
-
-// Local packages
+import org.apache.log4j.Logger;
 import org.mulgara.content.Content;
 import org.mulgara.content.ContentHandler;
 import org.mulgara.content.ContentHandlerException;
 import org.mulgara.content.ModifiedException;
-import org.mulgara.content.NotModifiedException;
+import org.mulgara.query.rdf.MimeTypes;
 import org.mulgara.resolver.spi.ResolverSession;
 import org.mulgara.resolver.spi.Statements;
 
@@ -66,59 +64,32 @@
   @SuppressWarnings("unused")
   private static Logger logger = Logger.getLogger(N3ContentHandler.class);
 
-  /**
-   * The MIME type of RDF/XML.
-   */
-  private static final MimeType APPLICATION_N3;
-  private static final MimeType TEXT_RDF_N3;
+  private static final Collection<MimeType> mimeTypes = 
+    Collections.unmodifiableList(Arrays.asList(MimeTypes.APPLICATION_N3, MimeTypes.TEXT_RDF_N3, MimeTypes.TEXT_TURTLE));
 
-  static {
-    try {
-      APPLICATION_N3 = new MimeType("application", "n3");
-      TEXT_RDF_N3    = new MimeType("text", "rdf+n3");
-    } catch (MimeTypeParseException e) {
-      throw new ExceptionInInitializerError(e);
-    }
-  }
+  private static final Collection<String> fileExts =
+    Collections.unmodifiableList(Arrays.asList("n3", "nt", "ttl"));
 
   //
   // Methods implementing ContentHandler
   //
 
-  public Statements parse(Content content, ResolverSession resolverSession) throws ContentHandlerException {
-    return new N3Statements(content, resolverSession);
+  /* (non-Javadoc)
+   * @see org.mulgara.content.ContentHandler#getContentTypes()
+   */
+  public Collection<MimeType> getContentTypes() {
+    return mimeTypes;
   }
 
-  /**
-   * @return <code>true</code> if the file part of the URI has an
-   *   <code>.n3</code>, <code>.nt</code> or <code>.rdf</code> extension
+  /* (non-Javadoc)
+   * @see org.mulgara.content.ContentHandler#getFileExtensions()
    */
-  public boolean canParse(Content content) throws NotModifiedException {
-    // We definitely can parse anything of MIME type application/rdf+xml
-    MimeType contentType = content.getContentType();
-    if (contentType != null && (APPLICATION_N3.match(contentType) || TEXT_RDF_N3.match(contentType))) {
-      return true;
-    }
+  public Collection<String> getFileExtensions() {
+    return fileExts;
+  }
 
-    // Don't know about this MIME type, so sniff at the pathname extension
-    // We're breaking the WWW architectural principle of URI Opacity by doing
-    // so (see http://www.w3.org/TR/webarch/#uri-opacity).
-
-    if (content.getURI() == null) {
-      return false;
-    }
-
-    // Obtain the path part of the URI
-    String path = content.getURI().getPath();
-    if (path == null) {
-      return false;
-    }
-    assert path != null;
-
-    // We recognize a fixed set of extensions
-    return path.endsWith(".n3") ||
-           path.endsWith(".nt") ||
-           path.endsWith(".ttl");
+  public Statements parse(Content content, ResolverSession resolverSession) throws ContentHandlerException {
+    return new N3Statements(content, resolverSession);
   }
 
   /**

Modified: trunk/src/jar/content-n3/java/org/mulgara/content/n3/Parser.java
===================================================================
--- trunk/src/jar/content-n3/java/org/mulgara/content/n3/Parser.java	2011-07-19 18:22:41 UTC (rev 2009)
+++ trunk/src/jar/content-n3/java/org/mulgara/content/n3/Parser.java	2011-07-19 20:28:14 UTC (rev 2010)
@@ -423,13 +423,14 @@
 
   private URIReference toURIReference(String string) {
     try {
-      return new URIReferenceImpl(new URI(string));
+      return toURIReference(new URI(string));
     } catch (URISyntaxException e) {
-      throw new Error("Invalid URI reference generated", e);
+      throw new RuntimeException("Invalid URI reference generated", e);
     }
   }
 
   private URIReference toURIReference(URI u) {
+    if (!u.isAbsolute() && baseURI != null) u = baseURI.resolve(u);
     return new URIReferenceImpl(u);
   }
 

Modified: trunk/src/jar/content-rdfa/java/org/mulgara/content/rdfa/RdfaContentHandler.java
===================================================================
--- trunk/src/jar/content-rdfa/java/org/mulgara/content/rdfa/RdfaContentHandler.java	2011-07-19 18:22:41 UTC (rev 2009)
+++ trunk/src/jar/content-rdfa/java/org/mulgara/content/rdfa/RdfaContentHandler.java	2011-07-19 20:28:14 UTC (rev 2010)
@@ -18,18 +18,18 @@
 
 // Java packages
 // Java 2 enterprise packages
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+
 import javax.activation.MimeType;
-import javax.activation.MimeTypeParseException;
 
-// Third party packages
-import org.apache.log4j.Logger;  // Apache Log4J
-
-// Local packages
+import org.apache.log4j.Logger;
 import org.mulgara.content.Content;
 import org.mulgara.content.ContentHandler;
 import org.mulgara.content.ContentHandlerException;
 import org.mulgara.content.ModifiedException;
-import org.mulgara.content.NotModifiedException;
+import org.mulgara.query.rdf.MimeTypes;
 import org.mulgara.resolver.spi.ResolverSession;
 import org.mulgara.resolver.spi.Statements;
 
@@ -44,27 +44,37 @@
   @SuppressWarnings("unused")
   private static Logger logger = Logger.getLogger(RdfaContentHandler.class);
 
-  /** The MIME type for XHTML documents */
-  static final MimeType XHTML_MIME;
-
-  /** The MIME type for HTML documents */
-  static final MimeType HTML_MIME;
-
   /** The filename extension for XHTML documents */
   static final String XHTML_EXT = ".xhtml";
 
   /** The filename extension for HTML documents */
   static final String HTML_EXT = ".html";
 
-  static {
-    try {
-      XHTML_MIME = new MimeType("application", "xhtml+xml");
-      HTML_MIME = new MimeType("text", "html");
-    } catch (MimeTypeParseException e) {
-      throw new Error("Mime initialization error");
-    }
+  /** Supported content types */
+  private static final Collection<MimeType> mimeTypes = 
+    Collections.unmodifiableList(Arrays.asList(MimeTypes.APPLICATION_XHTML, MimeTypes.TEXT_HTML));
+
+  /** Supported file types */
+  private static final Collection<String> fileExts =
+    Collections.unmodifiableList(Arrays.asList("xhtml", "html"));
+  
+  /* (non-Javadoc)
+   * @see org.mulgara.content.ContentHandler#getContentTypes()
+   */
+  public Collection<MimeType> getContentTypes() {
+    return mimeTypes;
   }
 
+  /* (non-Javadoc)
+   * @see org.mulgara.content.ContentHandler#getFileExtensions()
+   */
+  public Collection<String> getFileExtensions() {
+    return fileExts;
+  }
+
+  /* (non-Javadoc)
+   * @see org.mulgara.content.ContentHandler#parse(org.mulgara.content.Content, org.mulgara.resolver.spi.ResolverSession)
+   */
   public Statements parse(Content content, ResolverSession resolverSession) throws ContentHandlerException {
     try {
       return new RdfaStatements(resolverSession, content);
@@ -74,25 +84,6 @@
   }
 
   /**
-   * @return <code>true</code> if the file part of the URI has an
-   *   <code>.n3</code>, <code>.nt</code> or <code>.rdf</code> extension
-   */
-  public boolean canParse(Content content) throws NotModifiedException {
-    // We definitely can parse anything of MIME type application/rdf+xml
-    MimeType contentType = content.getContentType();
-    if (contentType != null && (HTML_MIME.match(contentType) || XHTML_MIME.match(contentType))) {
-      return true;
-    }
-
-    if (content.getURI() == null) return false;
-
-    // Obtain the path part of the URI
-    String path = content.getURI().getPath();
-    if (path == null) return false;
-    return path.endsWith(".html") || path.endsWith(".xhtml");
-  }
-
-  /**
    * Cannot serialize RDF into HTML.
    */
   public void serialize(Statements      statements,

Modified: trunk/src/jar/content-rdfa/java/org/mulgara/content/rdfa/RdfaStatementsUnitTest.java
===================================================================
--- trunk/src/jar/content-rdfa/java/org/mulgara/content/rdfa/RdfaStatementsUnitTest.java	2011-07-19 18:22:41 UTC (rev 2009)
+++ trunk/src/jar/content-rdfa/java/org/mulgara/content/rdfa/RdfaStatementsUnitTest.java	2011-07-19 20:28:14 UTC (rev 2010)
@@ -26,7 +26,6 @@
 import java.util.List;
 
 import javax.activation.MimeType;
-import javax.activation.MimeTypeParseException;
 
 import junit.framework.Test;
 import junit.framework.TestCase;
@@ -41,6 +40,7 @@
 import org.jrdf.graph.Triple;
 import org.jrdf.graph.URIReference;
 import org.mulgara.content.Content;
+import org.mulgara.query.rdf.MimeTypes;
 import org.mulgara.resolver.spi.ResolverSession;
 import org.mulgara.resolver.spi.Statements;
 import org.mulgara.resolver.spi.TestResolverSession;
@@ -226,11 +226,7 @@
     this.content = content;
   }
 
-  public MimeType getContentType()                       {
-    try {
-      return new MimeType("text", "html");
-    } catch (MimeTypeParseException e) { return null; }
-  }
+  public MimeType getContentType()                       { return MimeTypes.TEXT_HTML; }
   public URI getURI()                                    { return URI.create("http://examples.tobyinkster.co.uk/hcard"); }
   public String getURIString()                           { return "http://examples.tobyinkster.co.uk/hcard"; }
   public OutputStream newOutputStream()                  { return null; }

Modified: trunk/src/jar/content-rdfa/java/org/mulgara/content/rdfa/StatementParser.java
===================================================================
--- trunk/src/jar/content-rdfa/java/org/mulgara/content/rdfa/StatementParser.java	2011-07-19 18:22:41 UTC (rev 2009)
+++ trunk/src/jar/content-rdfa/java/org/mulgara/content/rdfa/StatementParser.java	2011-07-19 20:28:14 UTC (rev 2010)
@@ -51,6 +51,7 @@
 import org.mulgara.query.TuplesException;
 import org.mulgara.query.rdf.BlankNodeImpl;
 import org.mulgara.query.rdf.LiteralImpl;
+import org.mulgara.query.rdf.MimeTypes;
 import org.mulgara.query.rdf.TripleImpl;
 import org.mulgara.query.rdf.URIReferenceImpl;
 import org.mulgara.resolver.spi.ResolverSession;
@@ -416,8 +417,8 @@
   private Format getType(Content c) throws NotModifiedException {
     MimeType t = c.getContentType();
     if (t != null) {
-      if (RdfaContentHandler.XHTML_MIME.match(t)) return Format.XHTML;
-      if (RdfaContentHandler.HTML_MIME.match(t)) return Format.HTML;
+      if (MimeTypes.APPLICATION_XHTML.match(t)) return Format.XHTML;
+      if (MimeTypes.TEXT_HTML.match(t)) return Format.HTML;
     }
     String loc = c.getURIString();
     if (loc != null) {

Modified: trunk/src/jar/content-rdfxml/java/org/mulgara/content/rdfxml/RDFXMLContentHandler.java
===================================================================
--- trunk/src/jar/content-rdfxml/java/org/mulgara/content/rdfxml/RDFXMLContentHandler.java	2011-07-19 18:22:41 UTC (rev 2009)
+++ trunk/src/jar/content-rdfxml/java/org/mulgara/content/rdfxml/RDFXMLContentHandler.java	2011-07-19 20:28:14 UTC (rev 2010)
@@ -30,18 +30,20 @@
 // Java 2 standard packages
 import java.io.IOException;
 import java.io.OutputStreamWriter;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
 
 import javax.activation.MimeType;
-import javax.activation.MimeTypeParseException;
 
 import org.apache.log4j.Logger;
 import org.mulgara.content.Content;
 import org.mulgara.content.ContentHandler;
 import org.mulgara.content.ContentHandlerException;
 import org.mulgara.content.ModifiedException;
-import org.mulgara.content.NotModifiedException;
 import org.mulgara.content.rdfxml.writer.RDFXMLWriter;
 import org.mulgara.query.QueryException;
+import org.mulgara.query.rdf.MimeTypes;
 import org.mulgara.resolver.spi.ResolverSession;
 import org.mulgara.resolver.spi.Statements;
 
@@ -66,24 +68,32 @@
   private static Logger logger =
     Logger.getLogger(RDFXMLContentHandler.class.getName());
 
-  /**
-   * The MIME type of RDF/XML.
-   */
-  private static final MimeType APPLICATION_RDF_XML;
+  /** Supported content types */
+  private static final Collection<MimeType> mimeTypes = 
+    Collections.unmodifiableList(Arrays.asList(MimeTypes.APPLICATION_RDF_XML));
 
-  static {
-    try {
-      APPLICATION_RDF_XML = new MimeType("application", "rdf+xml");
-    }
-    catch (MimeTypeParseException e) {
-      throw new ExceptionInInitializerError(e);
-    }
-  }
-
+  /** Supported file types */
+  private static final Collection<String> fileExts =
+    Collections.unmodifiableList(Arrays.asList("rdf", "rdfs", "owl"));
+  
   //
   // Methods implementing ContentHandler
   //
 
+  /* (non-Javadoc)
+   * @see org.mulgara.content.ContentHandler#getContentTypes()
+   */
+  public Collection<MimeType> getContentTypes() {
+    return mimeTypes;
+  }
+
+  /* (non-Javadoc)
+   * @see org.mulgara.content.ContentHandler#getFileExtensions()
+   */
+  public Collection<String> getFileExtensions() {
+    return fileExts;
+  }
+
   public Statements parse(Content content, ResolverSession resolverSession)
                           throws ContentHandlerException
   {
@@ -91,39 +101,6 @@
   }
 
   /**
-   * @return <code>true</code> if the file part of the URI has an
-   *   <code>.n3</code>, <code>.nt</code> or <code>.rdf</code> extension
-   */
-  public boolean canParse(Content content) throws NotModifiedException
-  {
-    // We definitely can parse anything of MIME type application/rdf+xml
-    MimeType contentType = content.getContentType();
-    if (contentType != null && APPLICATION_RDF_XML.match(contentType)) {
-      return true;
-    }
-
-    // Don't know about this MIME type, so sniff at the pathname extension
-    // We're breaking the WWW architectural principle of URI Opacity by doing
-    // so (see http://www.w3.org/TR/webarch/#uri-opacity).
-
-    if (content.getURI() == null) {
-      return false;
-    }
-
-    // Obtain the path part of the URI
-    String path = content.getURI().getPath();
-    if (path == null) {
-      return false;
-    }
-    assert path != null;
-    
-    // We recognize a fixed set of extensions
-    return path.endsWith(".owl") ||
-           path.endsWith(".rdf") ||
-           path.endsWith(".rdfs");
-  }
-
-  /**
    * @throws ContentHandlerException  {@inheritDoc}; this particular
    *   implementation doesn't implement this method and will always throw the
    *   exception

Modified: trunk/src/jar/content-rdfxml/java/org/mulgara/content/rdfxml/writer/RDFXMLWriterUnitTest.java
===================================================================
--- trunk/src/jar/content-rdfxml/java/org/mulgara/content/rdfxml/writer/RDFXMLWriterUnitTest.java	2011-07-19 18:22:41 UTC (rev 2009)
+++ trunk/src/jar/content-rdfxml/java/org/mulgara/content/rdfxml/writer/RDFXMLWriterUnitTest.java	2011-07-19 20:28:14 UTC (rev 2010)
@@ -107,9 +107,6 @@
       File rdfFile = new File(RDF_FILE);
       Content rdfContent = new FileContent(rdfFile);
 
-      //check to be sure test data is ok
-      assertTrue("Bad test data. Not parsable by RDF/XML Content Handler.",
-          contentHandler.canParse(rdfContent));
       ResolverSession session1 = new TestResolverSession();
       Statements statements1 = contentHandler.parse(rdfContent, session1);
       //write them
@@ -119,8 +116,6 @@
       rdfContent = new FileContent(tmpFile1);
       ResolverSession session2 = new TestResolverSession();
       //output should be parsable
-      assertTrue("Output is not parsable by RDF/XML Content Handler.",
-          contentHandler.canParse(rdfContent));
       Statements statements2 = contentHandler.parse(rdfContent, session2);
       tmpFile2 = writeStatements(statements2, session2);
 

Modified: trunk/src/jar/content-rlog/java/org/mulgara/content/rlog/RlogContentHandler.java
===================================================================
--- trunk/src/jar/content-rlog/java/org/mulgara/content/rlog/RlogContentHandler.java	2011-07-19 18:22:41 UTC (rev 2009)
+++ trunk/src/jar/content-rlog/java/org/mulgara/content/rlog/RlogContentHandler.java	2011-07-19 20:28:14 UTC (rev 2010)
@@ -21,19 +21,18 @@
 import java.io.BufferedWriter;
 import java.io.OutputStreamWriter;
 import java.io.Writer;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
 
 import javax.activation.MimeType;
-import javax.activation.MimeTypeParseException;
 
-// Third party packages
-import org.apache.log4j.Logger;  // Apache Log4J
-
-// Local packages
+import org.apache.log4j.Logger;
 import org.mulgara.content.Content;
 import org.mulgara.content.ContentHandler;
 import org.mulgara.content.ContentHandlerException;
 import org.mulgara.content.ModifiedException;
-import org.mulgara.content.NotModifiedException;
+import org.mulgara.query.rdf.MimeTypes;
 import org.mulgara.resolver.spi.ResolverSession;
 import org.mulgara.resolver.spi.Statements;
 
@@ -49,51 +48,36 @@
   @SuppressWarnings("unused")
   private static Logger logger = Logger.getLogger(RlogContentHandler.class);
 
-  /** The MIME type of RLog. */
-  private static final MimeType TEXT_RLOG;
+  /** Supported content types */
+  private static final Collection<MimeType> mimeTypes = 
+    Collections.unmodifiableList(Arrays.asList(MimeTypes.TEXT_RLOG));
 
-  static {
-    try {
-      TEXT_RLOG    = new MimeType("text", "rlog");
-    } catch (MimeTypeParseException e) {
-      throw new ExceptionInInitializerError(e);
-    }
+  /** Supported file types */
+  private static final Collection<String> fileExts =
+    Collections.unmodifiableList(Arrays.asList("dl", "rl", "rlog"));
+
+  /* (non-Javadoc)
+   * @see org.mulgara.content.ContentHandler#getContentTypes()
+   */
+  public Collection<MimeType> getContentTypes() {
+    return mimeTypes;
   }
 
+  /* (non-Javadoc)
+   * @see org.mulgara.content.ContentHandler#getFileExtensions()
+   */
+  public Collection<String> getFileExtensions() {
+    return fileExts;
+  }
 
+  /* (non-Javadoc)
+   * @see org.mulgara.content.ContentHandler#parse(org.mulgara.content.Content, org.mulgara.resolver.spi.ResolverSession)
+   */
   public Statements parse(Content content, ResolverSession resolverSession) throws ContentHandlerException {
     return new RlogStatements(content, resolverSession);
   }
 
-
   /**
-   * Tests if this content handler can handle the provided content.
-   * @return <code>true</code> if the file part of the URI has an
-   *         <code>.rl</code>, or <code>.rlog</code> extension
-   * @throws NotModifiedException If the content has been accessed before
-   *         and not modified in the meantime.
-   */
-  public boolean canParse(Content content) throws NotModifiedException {
-    // We definitely can parse anything of MIME type text/rlog
-    MimeType contentType = content.getContentType();
-    if (contentType != null && (TEXT_RLOG.match(contentType))) return true;
-
-    // Don't know about this MIME type, so sniff at the pathname extension
-    // We're breaking the WWW architectural principle of URI Opacity by doing
-    // so (see http://www.w3.org/TR/webarch/#uri-opacity).
-
-    if (content.getURI() == null) return false;
-
-    // Obtain the path part of the URI
-    String path = content.getURI().getPath();
-    if (path == null) return false;
-
-    // We recognize a fixed set of extensions
-    return  path.endsWith(".dl") || path.endsWith(".rl") || path.endsWith(".rlog");
-  }
-
-
-  /**
    * Writes out the statements in basic RLog format.
    */
   public void serialize(Statements statements, Content content, ResolverSession resolverSession)

Added: trunk/src/jar/query/java/org/mulgara/query/rdf/MimeTypes.java
===================================================================
--- trunk/src/jar/query/java/org/mulgara/query/rdf/MimeTypes.java	                        (rev 0)
+++ trunk/src/jar/query/java/org/mulgara/query/rdf/MimeTypes.java	2011-07-19 20:28:14 UTC (rev 2010)
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2011 Revelytix, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mulgara.query.rdf;
+
+import javax.activation.MimeType;
+import javax.activation.MimeTypeParseException;
+
+/**
+ * Central location for defining MIME content type constants.
+ * 
+ * @author Alex Hall
+ * @created Jul 19, 2011
+ */
+public class MimeTypes {
+  
+  /** MIME type for N3 */
+  public static final MimeType APPLICATION_N3;
+  
+  /** The MIME type of RDF/XML. */
+  public static final MimeType APPLICATION_RDF_XML;
+
+  /** The MIME type for XHTML documents */
+  public static final MimeType APPLICATION_XHTML;
+
+  /** The MIME type of MP3. */
+  public static final MimeType AUDIO_MPEG;
+
+  /** The MIME type for HTML documents */
+  public static final MimeType TEXT_HTML;
+
+  /** MIME type for N3 */
+  public static final MimeType TEXT_RDF_N3;
+  
+  /** The MIME type of RLog. */
+  public static final MimeType TEXT_RLOG;
+
+  /** MIME type for Turtle */
+  public static final MimeType TEXT_TURTLE;
+
+  static {
+    try {
+      APPLICATION_N3      = new MimeType("application", "n3");
+      APPLICATION_RDF_XML = new MimeType("application", "rdf+xml");
+      APPLICATION_XHTML = new MimeType("application", "xhtml+xml");
+      AUDIO_MPEG = new MimeType("audio", "mpeg");
+      TEXT_HTML = new MimeType("text", "html");
+      TEXT_RDF_N3         = new MimeType("text", "rdf+n3");
+      TEXT_RLOG    = new MimeType("text", "rlog");
+      TEXT_TURTLE         = new MimeType("text", "turtle");
+    }
+    catch (MimeTypeParseException e) {
+      throw new ExceptionInInitializerError(e);
+    }
+  }
+
+}

Modified: trunk/src/jar/resolver/java/org/mulgara/resolver/BasicDatabaseSessionUnitTest.java
===================================================================
--- trunk/src/jar/resolver/java/org/mulgara/resolver/BasicDatabaseSessionUnitTest.java	2011-07-19 18:22:41 UTC (rev 2009)
+++ trunk/src/jar/resolver/java/org/mulgara/resolver/BasicDatabaseSessionUnitTest.java	2011-07-19 20:28:14 UTC (rev 2010)
@@ -38,9 +38,6 @@
 import java.util.Collections;
 import java.util.List;
 
-import javax.activation.MimeType;
-import javax.activation.MimeTypeParseException;
-
 import junit.framework.Test;
 import junit.framework.TestCase;
 import junit.framework.TestSuite;
@@ -61,6 +58,7 @@
 import org.mulgara.query.UnconstrainedAnswer;
 import org.mulgara.query.Variable;
 import org.mulgara.query.rdf.LiteralImpl;
+import org.mulgara.query.rdf.MimeTypes;
 import org.mulgara.query.rdf.Mulgara;
 import org.mulgara.query.rdf.URIReferenceImpl;
 import org.mulgara.server.Session;
@@ -130,7 +128,7 @@
     suite.addTest(new BasicDatabaseSessionUnitTest("testSetModel"));
     suite.addTest(new BasicDatabaseSessionUnitTest("testQuery2"));
     suite.addTest(new BasicDatabaseSessionUnitTest("testSetModelRdfXml"));
-    //suite.addTest(new BasicDatabaseSessionUnitTest("testSetModelN3"));
+    suite.addTest(new BasicDatabaseSessionUnitTest("testSetModelN3"));
 
     return suite;
   }
@@ -341,19 +339,6 @@
   private static PredicateNode TEST_PREDICATE = new URIReferenceImpl(URI.create("http://example.org/ns#p"));
   private static ObjectNode TEST_OBJECT = new LiteralImpl("foo");
   
-  private static final MimeType APPLICATION_RDF_XML;
-  private static final MimeType APPLICATION_N3;
-
-  static {
-    try {
-      APPLICATION_RDF_XML = new MimeType("application", "rdf+xml");
-      APPLICATION_N3 = new MimeType("application", "n3");
-    }
-    catch (MimeTypeParseException e) {
-      throw new ExceptionInInitializerError(e);
-    }
-  }
-  
   public void testSetModelRdfXml() throws Exception {
     URI graph = URI.create("test:graph");
     URI base = URI.create("http://example.org/base/");
@@ -362,7 +347,7 @@
       session.createModel(graph, null);
       FileInputStream in = new FileInputStream("data/rel-uri.rdf");
       try {
-        session.setModel(in, graph, base, APPLICATION_RDF_XML);
+        session.setModel(in, graph, base, MimeTypes.APPLICATION_RDF_XML);
       } finally {
         in.close();
       }
@@ -374,6 +359,8 @@
   }
   
   public void testSetModelN3() throws Exception {
+    database.addContentHandler("org.mulgara.content.n3.N3ContentHandler");
+    
     URI graph = URI.create("test:graph");
     URI base = URI.create("http://example.org/base/");
     Session session = database.newSession();
@@ -381,7 +368,7 @@
       session.createModel(graph, null);
       FileInputStream in = new FileInputStream("data/rel-uri.ttl");
       try {
-        session.setModel(in, graph, base, APPLICATION_N3);
+        session.setModel(in, graph, base, MimeTypes.TEXT_TURTLE);
       } finally {
         in.close();
       }

Modified: trunk/src/jar/resolver/java/org/mulgara/resolver/ContentHandlerManagerImpl.java
===================================================================
--- trunk/src/jar/resolver/java/org/mulgara/resolver/ContentHandlerManagerImpl.java	2011-07-19 18:22:41 UTC (rev 2009)
+++ trunk/src/jar/resolver/java/org/mulgara/resolver/ContentHandlerManagerImpl.java	2011-07-19 20:28:14 UTC (rev 2010)
@@ -30,14 +30,18 @@
 // Java 2 standard packages
 import java.lang.reflect.InvocationTargetException;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.List;
 
+import javax.activation.MimeType;
+
 import org.apache.log4j.Logger;
 import org.mulgara.content.Content;
 import org.mulgara.content.ContentHandler;
 import org.mulgara.content.ContentHandlerException;
 import org.mulgara.content.ContentHandlerManager;
 import org.mulgara.content.NotModifiedException;
+import org.mulgara.content.ValidatingContentHandler;
 import org.mulgara.query.QueryException;
 import org.mulgara.resolver.spi.ResolverSession;
 import org.mulgara.resolver.spi.Statements;
@@ -96,14 +100,72 @@
       throw new IllegalArgumentException("Content is null");
     }
 
-    for (ContentHandler contentHandler: contentHandlerList) {
-      if (contentHandler.canParse(content)) {
-        if ( logger.isInfoEnabled()) {
-          logger.info("Determined " + contentHandler.getClass() + " can parse content\n" + new StackTrace());
+    if (logger.isDebugEnabled()) {
+      StringBuilder sb = new StringBuilder("Searching for handler for content ");
+      if (content.getURI() != null) sb.append("<").append(content.getURI()).append("> ");
+      if (content.isStreaming()) sb.append("from stream ");
+      if (content.getContentType() != null) sb.append("with type ").append(content.getContentType());
+      sb.append(new StackTrace());
+      logger.debug(sb.toString());
+    }
+    
+    // Prefer supplied MIME type when choosing handler.
+    MimeType contentType = content.getContentType();
+    if (contentType != null) {
+      for (ContentHandler h : contentHandlerList) {
+        Collection<MimeType> types = h.getContentTypes();
+        if (types != null) {
+          for (MimeType type : types) {
+            if (type != null && type.match(contentType)) {
+              if (logger.isDebugEnabled()) logger.debug(h.getClass() + " handles type " + contentType);
+              return h;
+            }
+          }
         }
-        return contentHandler;
       }
     }
+    
+    // Content type wasn't specified or didn't match anything, so fall back to filename extension.
+    if (content.getURI() != null) {
+      // Peek at the file extension, if there is one.
+      String extension = null;
+      String path = content.getURI().getPath();
+      if (path != null) {
+        for (int i = path.length() - 1; i >= 0; i--) {
+          char c = path.charAt(i);
+          if (c == '.') {
+            extension = path.substring(i + 1);
+            break;
+          } else if (c == '/') {
+            break;
+          }
+        }
+      }
+      // Check for a handler that supports the extension.
+      if (extension != null && extension.length() > 0) {
+        for (ContentHandler h : contentHandlerList) {
+          Collection<String> fileExts = h.getFileExtensions();
+          if (fileExts != null) {
+            for (String fileExt : fileExts) {
+              if (extension.equalsIgnoreCase(fileExt)) {
+                if (logger.isDebugEnabled()) logger.debug(h.getClass() + " handles file extension '" + extension + "'");
+                return h;
+              }
+            }
+          }
+        }
+      }
+    }
+    
+    // This is a hack because the MBox content handler does a blind parse instead of properly
+    // checking for content type or file extension.
+    if (!content.isStreaming()) {
+      for (ContentHandler h : contentHandlerList) {
+        if (h instanceof ValidatingContentHandler && ((ValidatingContentHandler)h).validate(content)) {
+          if (logger.isDebugEnabled()) logger.debug(h.getClass() + " validated content.");
+        }
+      }
+    }
 
     if (defaultHandler == null) {
       if (content.getURI() != null) {

Modified: trunk/src/jar/resolver/java/org/mulgara/resolver/SetGraphOperation.java
===================================================================
--- trunk/src/jar/resolver/java/org/mulgara/resolver/SetGraphOperation.java	2011-07-19 18:22:41 UTC (rev 2009)
+++ trunk/src/jar/resolver/java/org/mulgara/resolver/SetGraphOperation.java	2011-07-19 20:28:14 UTC (rev 2010)
@@ -70,12 +70,11 @@
   /** Logger. */
   private static final Logger logger = Logger.getLogger(SetGraphOperation.class.getName());
 
-  private final URI         srcModelURI;
-  private final URI         destModelURI;
+  private final URI         sourceUri;
+  private final URI         destinationUri;
   private final InputStream inputStream;
   private final MimeType    contentType;
 
-  private Content               content;
   private final ContentHandlerManager contentHandlers;
 
   /**
@@ -89,21 +88,20 @@
   //
 
   /**
-   * Sole constructor.
-   *
-   * @param srcModelURI the {@link URI} of the model to be redefined
-   * @param destModelURI the {@link URI} of the model to be redefined
-   * @param inputStream  a stream containing the content at the <var>srcModelURI</var>
-   *   or <code>null</code> if the content must be fetched
+   * @param sourceUri The URI of the new content, which might double as its location.
+   * @param destinationUri The URI of the graph being set.
+   * @param inputStream The content to load, or <tt>null</tt> to de-reference the source URI.
+   * @param contentType The content type, or <tt>null</tt> if unknown.
+   * @param contentHandlers The content handler manager to find the correct parser.
    */
-  SetGraphOperation(URI         srcModelURI,
-                    URI         destModelURI,
+  SetGraphOperation(URI         sourceUri,
+                    URI         destinationUri,
                     InputStream inputStream,
                     MimeType    contentType,
                     ContentHandlerManager contentHandlers)
   {
-    this.srcModelURI      = srcModelURI;
-    this.destModelURI     = destModelURI;
+    this.sourceUri      = sourceUri;
+    this.destinationUri     = destinationUri;
     this.inputStream      = inputStream;
     this.contentType      = contentType;
     this.contentHandlers  = contentHandlers;
@@ -122,28 +120,25 @@
       throw new IllegalStateException("SetGraphOperation already executed.  Cannot reexecute.");
     }
 
+    Content content;
     // create a StreamContent if an inputStream has been supplied
     if (inputStream != null) {
       if (logger.isDebugEnabled()) {
-        logger.debug("Detected inputstream associated with " + srcModelURI );
+        logger.debug("Detected inputstream associated with " + sourceUri );
       }
-      if (srcModelURI == null) {
-        content = new StreamContent(inputStream, contentType);
-      } else {
-        content = new StreamContent(inputStream, srcModelURI);
-      }
+      content = new StreamContent(inputStream, sourceUri, contentType);
     } else {
       try {
-        content = ContentFactory.getContent(srcModelURI);
+        content = ContentFactory.getContent(sourceUri);
       } catch (ContentHandlerException ec) {
         throw new QueryException("Failed to find Content for uri", ec);
       }
     }
     
-    long destinationModel = systemResolver.localize(new URIReferenceImpl(destModelURI));
+    long destinationModel = systemResolver.localize(new URIReferenceImpl(destinationUri));
     try {
-      long sourceModel = systemResolver.localize(new URIReferenceImpl(srcModelURI, false));
-      if (destinationModel == sourceModel) throw new QueryException("Identical source and destination: " + destModelURI);
+      long sourceModel = systemResolver.localize(new URIReferenceImpl(sourceUri, false));
+      if (destinationModel == sourceModel) throw new QueryException("Identical source and destination: " + destinationUri);
     } catch (Exception e) {
       // source and destinations cannot be equal, so ignore
     }
@@ -162,12 +157,12 @@
         new PersistentResolverSession(systemResolver));
 
     if (logger.isDebugEnabled()) {
-      logger.debug("Modifying " + destModelURI + " using " + destinationResolver);
+      logger.debug("Modifying " + destinationUri + " using " + destinationResolver);
     }
 
     destinationResolver.modifyModel(destinationModel, statements, true);
     if (logger.isDebugEnabled()) {
-      logger.debug("Modified " + destModelURI);
+      logger.debug("Modified " + destinationUri);
     }
 
     statementCount = statements.getRowCount();

Modified: trunk/src/jar/resolver/java/org/mulgara/resolver/StreamContent.java
===================================================================
--- trunk/src/jar/resolver/java/org/mulgara/resolver/StreamContent.java	2011-07-19 18:22:41 UTC (rev 2009)
+++ trunk/src/jar/resolver/java/org/mulgara/resolver/StreamContent.java	2011-07-19 20:28:14 UTC (rev 2010)
@@ -53,13 +53,13 @@
 class StreamContent implements Content {
 
   /** The wrapped uri to assist with InputStream content detection. */
-  private URI uri;
+  private final URI uri;
 
   /** The wrapped inputStream containing the source content. */
-  private InputStream inputStream;
+  private final InputStream inputStream;
 
   /** The content type of the stream, if provided. */
-  private MimeType contentType;
+  private final MimeType contentType;
 
   //
   // Constructor
@@ -67,37 +67,19 @@
 
   /**
    * Wrap a {@link InputStream} as {@link Content}.
-   * A URI must be supplied to help determine the content
+   * Either a URI or a conten type must be supplied to help determine the content
    * of the inputstream.  
    */
-  StreamContent(InputStream inputStream, URI uri) {
+  StreamContent(InputStream inputStream, URI uri, MimeType contentType) {
     // Validate "file" parameter
     if (uri == null) {
       throw new IllegalArgumentException("Null \"uri\" parameter");
     }
-    if (inputStream == null) {
-      throw new IllegalArgumentException("Null \"inputStream\" parameter");
+    if (inputStream == null && contentType == null) {
+      throw new IllegalArgumentException("Must provide at least one of URI and contentType");
     }
     // Initialize fields
     this.uri = uri;
-    this.contentType = null;
-    this.inputStream = inputStream;
-  }
-
-  /**
-   * Wrap a {@link InputStream} as {@link Content}.
-   * The content type must be provided.  
-   */
-  StreamContent(InputStream inputStream, MimeType contentType) {
-    // Validate "file" parameter
-    if (contentType == null) {
-      throw new IllegalArgumentException("Null \"contentType\" parameter");
-    }
-    if (inputStream == null) {
-      throw new IllegalArgumentException("Null \"inputStream\" parameter");
-    }
-    // Initialize fields
-    this.uri = null;
     this.contentType = contentType;
     this.inputStream = inputStream;
   }

Modified: trunk/src/jar/resolver-spi/java/org/mulgara/content/ContentHandler.java
===================================================================
--- trunk/src/jar/resolver-spi/java/org/mulgara/content/ContentHandler.java	2011-07-19 18:22:41 UTC (rev 2009)
+++ trunk/src/jar/resolver-spi/java/org/mulgara/content/ContentHandler.java	2011-07-19 20:28:14 UTC (rev 2010)
@@ -28,6 +28,10 @@
 package org.mulgara.content;
 
 // Local packages
+import java.util.Collection;
+
+import javax.activation.MimeType;
+
 import org.mulgara.resolver.spi.ResolverSession;
 import org.mulgara.resolver.spi.Statements;
 
@@ -48,7 +52,20 @@
  * @licence <a href="{@docRoot}/../../LICENCE">Mozilla Public License v1.1</a>
  */
 public interface ContentHandler {
+  
   /**
+   * Determines the MIME type(s) supported by this content handler.
+   * @return The supported content type(s); never null but may be empty.
+   */
+  public Collection<MimeType> getContentTypes();
+  
+  /**
+   * Determines the filename extension(s) supported by this content handler.
+   * @return The supported filename extension(s); never null but may be empty.
+   */
+  public Collection<String> getFileExtensions();
+  
+  /**
    * Parse {@link Content} into {@link Statements}.
    *
    * Note that just because this method successfully returns a
@@ -73,22 +90,6 @@
     throws ContentHandlerException, NotModifiedException;
 
   /**
-   * Estimate whether the given content can be parsed by this handler.
-   *
-   * Typically, this is used to check for a known file extension in the
-   * <var>content</var> URI, even through doing so violates the WWW
-   * architectural principle of
-   * <a href="http://www.w3.org/TR/webarch/#uri-opacity">URI opacity</a>.
-   *
-   * @param content  a candidate for the <var>content</var> of the
-   *   {@link #parse} method
-   * @return whether the <var>content</var> is likely to be successfully parsed
-   * @throws NotModifiedException if we don't need to parse the
-   *   <var>content</var>, because it's already cached
-   */
-  public boolean canParse(Content content) throws NotModifiedException;
-
-  /**
    * Serialize {@link Statements} into {@link Content}.
    *
    * This method blocks until the serialization is complete.  It overwrites the

Added: trunk/src/jar/resolver-spi/java/org/mulgara/content/ValidatingContentHandler.java
===================================================================
--- trunk/src/jar/resolver-spi/java/org/mulgara/content/ValidatingContentHandler.java	                        (rev 0)
+++ trunk/src/jar/resolver-spi/java/org/mulgara/content/ValidatingContentHandler.java	2011-07-19 20:28:14 UTC (rev 2010)
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2011 Revelytix, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mulgara.content;
+
+/**
+ * Hack to account for the fact that the MBox content handler does a blind parse of the content
+ * to determine whether it can parse it. This fell out of the inversion of logic in choosing a
+ * content handler that resulted from eliminating the ContentHandler.canParse method.
+ * 
+ * @author Alex Hall
+ * @created Jul 19, 2011
+ */
+public interface ValidatingContentHandler extends ContentHandler {
+
+  /**
+   * Validate the given content, most likely based on a blind parse.
+   * @param content Some content.
+   * @return <tt>true</tt> if we can parse this content.
+   */
+  public boolean validate(Content content) throws NotModifiedException;
+}



More information about the Mulgara-svn mailing list