2 * Copyright (c) 2001-2007 Sun Microsystems, Inc. All rights reserved.
4 * The Sun Project JXTA(TM) Software License
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
9 * 1. Redistributions of source code must retain the above copyright notice,
10 * this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright notice,
13 * this list of conditions and the following disclaimer in the documentation
14 * and/or other materials provided with the distribution.
16 * 3. The end-user documentation included with the redistribution, if any, must
17 * include the following acknowledgment: "This product includes software
18 * developed by Sun Microsystems, Inc. for JXTA(TM) technology."
19 * Alternately, this acknowledgment may appear in the software itself, if
20 * and wherever such third-party acknowledgments normally appear.
22 * 4. The names "Sun", "Sun Microsystems, Inc.", "JXTA" and "Project JXTA" must
23 * not be used to endorse or promote products derived from this software
24 * without prior written permission. For written permission, please contact
25 * Project JXTA at http://www.jxta.org.
27 * 5. Products derived from this software may not be called "JXTA", nor may
28 * "JXTA" appear in their name, without prior written permission of Sun.
30 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
31 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
32 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SUN
33 * MICROSYSTEMS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
34 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
35 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
36 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
37 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
38 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
39 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
41 * JXTA is a registered trademark of Sun Microsystems, Inc. in the United
42 * States and other countries.
44 * Please see the license information page at :
45 * <http://www.jxta.org/project/www/license.html> for instructions on use of
46 * the license in source files.
48 * ====================================================================
50 * This software consists of voluntary contributions made by many individuals
51 * on behalf of Project JXTA. For more information on Project JXTA, please see
52 * http://www.jxta.org.
54 * This license is based on the BSD license adopted by the Apache Foundation.
57 package net.jxta.document;
60 import java.io.InputStream;
61 import java.io.Reader;
62 import java.util.HashMap;
63 import java.util.Hashtable;
66 import java.io.IOException;
67 import java.util.MissingResourceException;
69 import java.util.logging.Logger;
70 import java.util.logging.Level;
71 import net.jxta.logging.Logging;
73 import net.jxta.util.ClassFactory;
74 import net.jxta.endpoint.MessageElement;
75 import net.jxta.endpoint.TextMessageElement;
79 * A factory for constructing instances of {@link StructuredDocument}.
80 * Behind the scenes, it also provides for the registration of the mime-types
81 * and constructors needed to accomplish the construction. All supported
82 * mime-types will need to register their implementation in this factory.
84 * @see net.jxta.document.Document
85 * @see net.jxta.document.StructuredTextDocument
86 * @see net.jxta.document.StructuredDocument
87 * @see net.jxta.document.MimeMediaType
89 public final class StructuredDocumentFactory extends ClassFactory<MimeMediaType, StructuredDocumentFactory.Instantiator> {
94 private static final Logger LOG = Logger.getLogger(StructuredDocumentFactory.class.getName());
97 * Interface for instantiators of StructuredDocuments
99 public interface Instantiator {
102 * For mapping between extensions and MIME types.
104 class ExtensionMapping extends Object {
109 private final String extension;
112 * MIME type it maps to
114 private final MimeMediaType mimetype;
117 * default constructor
119 public ExtensionMapping(String extension, MimeMediaType mimetype) {
120 this.extension = extension;
121 this.mimetype = (null != mimetype) ? mimetype.intern() : null;
128 public boolean equals(Object target) {
129 if (this == target) {
133 if (target instanceof ExtensionMapping) {
134 ExtensionMapping likeMe = (ExtensionMapping) target;
136 if (!extension.equals(likeMe.extension)) {
140 if ((null == mimetype) && (null == likeMe.mimetype)) {
144 if ((null == mimetype) || (null == likeMe.mimetype)) {
148 return mimetype.equals(likeMe.mimetype);
158 public int hashCode() {
159 int hash = extension.hashCode();
161 if (null != mimetype) {
162 hash ^= mimetype.hashCode();
172 public String toString() {
173 return extension + " -> " + ((null != mimetype) ? mimetype.toString() : "<null>");
177 * Returns the extension which is part of this mapping.
179 * @return the extension which is part of this mapping.
181 public String getExtension() {
186 * Returns the MIME Media Type which is part of this mapping.
188 * @return the MIME Media Type which is part of this mapping.
190 public MimeMediaType getMimeMediaType() {
196 * Returns the MIME Media types supported by this this Document per
197 * {@link <a href="http://www.ietf.org/rfc/rfc2046.txt">IETF RFC 2046 <i>MIME : Media Types</i></a>}.
199 * <p/>JXTA does not currently support the 'Multipart' or 'Message'
202 * @return An array of MimeMediaType objects containing the MIME Media Type
205 MimeMediaType[] getSupportedMimeTypes();
208 * Returns the mapping of file extension and mime-types for this type
209 * of document. The default extension is mapped to the 'null' mime-type
210 * and should only be used if no other mapping matches.
212 * @return An array of objects containing file extensions
214 ExtensionMapping[] getSupportedFileExtensions();
217 * Create a new structured document of the type specified by doctype.
219 * @param mimeType The MIME type to be associated with this instance.
220 * the base type must be one of the types returned by
221 * <tt>getSupportedMimeTypes</tt>. Some implementations may accept
222 * parameters in the params section of the MIME type.
223 * @param doctype Type for the base node of the document.
224 * @return StructuredDocument instance.
226 StructuredDocument newInstance(MimeMediaType mimeType, String doctype);
229 * Create a new structured document of the type specified by doctype.
231 * @param mimeType The MIME type to be associated with this instance.
232 * The base type must be one of the types returned by
233 * <tt>getSupportedMimeTypes</tt>. Some implementations may accept
234 * parameters in the params section of the MIME type.
235 * @param doctype Type for the base node of the document.
236 * @param value Value for the base node of the document.
237 * @return {@link StructuredDocument} instance.
239 StructuredDocument newInstance(MimeMediaType mimeType, String doctype, String value);
242 * Create a structured document from a stream containing an appropriately serialized
243 * instance of the same document.
245 * @param mimeType The MIME type to be associated with this instance.
246 * The base type must be one of the types returned by
247 * <tt>getSupportedMimeTypes</tt>. Some implementations may accept
248 * parameters in the params section of the MIME type.
249 * @param source The {@code Inputstream} from which to read the
251 * @return {@link StructuredDocument} instance.
252 * @throws IOException Thrown for problems reading from the source.
254 StructuredDocument newInstance(MimeMediaType mimeType, InputStream source) throws IOException;
259 * Interface for instantiators of StructuredTextDocuments
261 public interface TextInstantiator extends Instantiator {
264 * Create a structured document from a Reader containing an appropriately serialized
265 * instance of the same document.
267 * @param mimeType The MIME type to be associated with this instance.
268 * The base type must be one of the types returned by
269 * <tt>getSupportedMimeTypes</tt>. Some implementations may accept
270 * parameters in the params section of the MIME type.
271 * @param source {@code Reader} from which to read the instance.
272 * @return {@link StructuredDocument} instance.
273 * @throws IOException Thrown for problems reading from the source.
275 StructuredDocument newInstance(MimeMediaType mimeType, Reader source) throws IOException;
279 * This class is a singleton. This is the instance that backs the
282 private static final StructuredDocumentFactory factory = new StructuredDocumentFactory();
285 * This is the map of mime-types and instantiators used by
286 * <CODE>newStructuredDocument</CODE>.
288 private final Map<MimeMediaType, Instantiator> encodings = new HashMap<MimeMediaType, Instantiator>();
291 * This is the map of extensions to mime-types used by
292 * {@link #getMimeTypeForFileExtension(String) }
294 private final Map<String, MimeMediaType> extToMime = new HashMap<String, MimeMediaType>();
297 * This is the map of mime-types to extensions used by
298 * {@link #getFileExtensionForMimeType(MimeMediaType mimetype) }
300 private final Map<MimeMediaType, String> mimeToExt = new HashMap<MimeMediaType, String>();
303 * If true then the pre-defined set of StructuredDocument sub-classes has
304 * been registered from the property containing them.
306 private boolean loadedProperty = false;
309 * Private constructor. This class is not meant to be instantiated except
313 private StructuredDocumentFactory() {}
316 * Registers the pre-defined set of StructuredDocument sub-classes so that
317 * this factory can construct them.
319 * @return true if at least one of the StructuredDocument sub-classes could
320 * be registered otherwise false.
322 private synchronized boolean loadProviders() {
323 if (factory.loadedProperty) {
327 factory.loadedProperty = registerProviders(StructuredDocument.class.getName());
329 return factory.loadedProperty;
336 protected Map<MimeMediaType, Instantiator> getAssocTable() {
344 protected Class<MimeMediaType> getClassForKey() {
345 return MimeMediaType.class;
352 protected Class<Instantiator> getClassOfInstantiators() {
353 // our key is the doctype names.
354 return Instantiator.class;
360 * <p/>We override the standard implementation to get the MIME type from
361 * the class and use that as the key to register the class with the factory.
363 * @param className The class name which will be registered.
364 * @return boolean true if the class was registered otherwise false.
367 protected boolean registerAssoc(String className) {
368 boolean registeredSomething = false;
370 LOG.finer("Registering : " + className);
373 Class docClass = Class.forName(className);
375 Instantiator instantiator = (Instantiator) docClass.getField("INSTANTIATOR").get(null);
377 MimeMediaType[] mimeTypes = instantiator.getSupportedMimeTypes();
379 for (int eachType = 0; eachType < mimeTypes.length; eachType++) {
380 LOG.finer(" Registering Type : " + mimeTypes[eachType].getMimeMediaType());
381 registeredSomething |= registerInstantiator(mimeTypes[eachType], instantiator);
383 } catch (Exception all) {
384 if (Logging.SHOW_WARNING && LOG.isLoggable(Level.WARNING)) {
385 LOG.log(Level.WARNING, "Failed to register \'" + className + "\'", all);
389 return registeredSomething;
393 * Returns the preferred extension for a given mime-type. If there is no
394 * mapping or no preferred extension for this MIME type then <tt>null</tt> is
397 * @param mimetype the MimeMediaType we wish to know the file extension for.
398 * @return String containing the extension or null for mime-types with no
401 public static String getFileExtensionForMimeType(MimeMediaType mimetype) {
402 factory.loadProviders();
404 return factory.mimeToExt.get(mimetype.getBaseMimeMediaType());
408 * Returns the preferred mime-type for a given file extension. If there is
409 * no mapping then <tt>null</tt> is returned.
411 * @param extension The extension we wish to know the mime-type for.
412 * @return MimeMediaType associated with this file extension.
414 public static MimeMediaType getMimeTypeForFileExtension(String extension) {
415 factory.loadProviders();
417 MimeMediaType result = factory.extToMime.get(extension);
423 * Register an instantiator object a mime-type of documents to be
426 * @param mimetype the mime-type associated.
427 * @param instantiator the instantiator that wants to be registered..
428 * @return boolean true if the instantiator for this mime-type is now
429 * registered. If there was already an instantiator this mime-type then
430 * false will be returned.
431 * @throws SecurityException there were permission problems registering
434 public static boolean registerInstantiator(MimeMediaType mimetype, Instantiator instantiator) {
435 boolean registered = factory.registerAssoc(mimetype.getBaseMimeMediaType(), instantiator);
438 Instantiator.ExtensionMapping[] extensions = instantiator.getSupportedFileExtensions();
440 for (int eachExt = 0; eachExt < extensions.length; eachExt++) {
441 if (null != extensions[eachExt].getMimeMediaType()) {
442 factory.extToMime.put(extensions[eachExt].getExtension(), extensions[eachExt].getMimeMediaType().intern());
444 factory.mimeToExt.put(extensions[eachExt].getMimeMediaType(), extensions[eachExt].getExtension());
446 // And the base version.
447 factory.mimeToExt.put(extensions[eachExt].getMimeMediaType().getBaseMimeMediaType(), extensions[eachExt].getExtension());
456 * Constructs an instance of {@link StructuredDocument} matching
457 * the mime-type specified by the <CODE>mimetype</CODE> parameter. The
458 * <CODE>doctype</CODE> parameter identifies the base type of the
459 * {@link StructuredDocument}.
461 * @param mimetype Specifies the mime media type to be associated with
462 * the {@link StructuredDocument} to be created.
463 * @param doctype Specifies the root type of the {@link StructuredDocument}
465 * @return StructuredDocument The instance of {@link StructuredDocument}
466 * or null if it could not be created.
467 * @throws NoSuchElementException invalid mime-media-type
469 public static StructuredDocument newStructuredDocument(MimeMediaType mimetype, String doctype) {
470 factory.loadProviders();
472 Instantiator instantiator = factory.getInstantiator(mimetype.getBaseMimeMediaType());
474 return instantiator.newInstance(mimetype, doctype);
478 * Constructs an instance of {@link StructuredDocument} matching
479 * the mime-type specified by the <CODE>mimetype</CODE> parameter. The
480 * <CODE>doctype</CODE> parameter identifies the base type of the
481 * {@link StructuredDocument}. Value supplies a value for the root
484 * @param mimetype Specifies the mime media type to be associated with
485 * the {@link StructuredDocument} to be created.
486 * @param doctype Specifies the root type of the {@link StructuredDocument}
488 * @param value Specifies a value for the root element.
489 * @return StructuredDocument The instance of {@link StructuredDocument}
490 * or null if it could not be created.
491 * @throws NoSuchElementException if the mime-type has not been registered.
493 public static StructuredDocument newStructuredDocument(MimeMediaType mimetype, String doctype, String value) {
494 factory.loadProviders();
496 Instantiator instantiator = factory.getInstantiator(mimetype.getBaseMimeMediaType());
498 return instantiator.newInstance(mimetype, doctype, value);
502 * Constructs an instance of {@link StructuredDocument} matching
503 * the mime-type specified by the <CODE>mimetype</CODE> parameter. The
504 * <CODE>doctype</CODE> parameter identifies the base type of the
505 * {@link StructuredDocument}.
507 * @param mimetype Specifies the mime media type to be associated with the
508 * {@link StructuredDocument} to be created.
509 * @param stream Contains an InputStream from which the document will be
511 * @return StructuredDocument The instance of {@link StructuredDocument}
512 * or null if it could not be created.
513 * @throws IOException If there is a problem reading from the stream.
514 * @throws NoSuchElementException if the mime-type has not been registered.
516 public static StructuredDocument newStructuredDocument(MimeMediaType mimetype, InputStream stream) throws IOException {
517 factory.loadProviders();
519 Instantiator instantiator = factory.getInstantiator(mimetype.getBaseMimeMediaType());
521 return instantiator.newInstance(mimetype, stream);
525 * Constructs an instance of {@link StructuredDocument} matching
526 * the mime-type specified by the <CODE>mimetype</CODE> parameter. The
527 * <CODE>doctype</CODE> parameter identifies the base type of the
528 * {@link StructuredDocument}.
530 * @param mimetype Specifies the mime media type to be associated with the
531 * {@link StructuredDocument} to be created.
532 * @param reader A Reader from which the document will be constructed.
533 * @return StructuredDocument The instance of {@link StructuredDocument}
534 * or {@code null} if it could not be created.
535 * @throws IOException If there is a problem reading from the stream.
536 * @throws NoSuchElementException if the mime-type has not been registered.
537 * @throws UnsupportedOperationException if the mime-type provided is not
538 * a text oriented MIME type.
540 public static StructuredDocument newStructuredDocument(MimeMediaType mimetype, Reader reader) throws IOException {
541 factory.loadProviders();
543 Instantiator instantiator = factory.getInstantiator(mimetype.getBaseMimeMediaType());
545 if (!(instantiator instanceof TextInstantiator)) {
546 // XXX 20020502 bondolo@jxta.org we could probably do something
547 // really inefficient that would allow it to work, but better not to.
548 // if ReaderInputStream existed, it would be easy to do.
549 if (Logging.SHOW_WARNING && LOG.isLoggable(Level.WARNING)) {
550 LOG.warning( "Document Class \'" + instantiator.getClass().getName() + "\' associated with \'" + mimetype
551 + "\' is not a text oriented document");
554 throw new UnsupportedOperationException( "Document Class '" + instantiator.getClass().getName()
555 + "' associated with '" + mimetype + "' is not a text oriented document");
558 return ((TextInstantiator) instantiator).newInstance(mimetype, reader);
562 * Constructs an instance of {@link StructuredDocument} based upon the
563 * content of the provided message element.
565 * @param element The message element from which to create the document.
566 * @return StructuredDocument The instance of {@link StructuredDocument}
567 * or null if it could not be created.
568 * @throws IOException If there is a problem reading from the stream.
569 * @throws NoSuchElementException if the mime-type has not been registered.
571 public static StructuredDocument newStructuredDocument(MessageElement element) throws IOException {
572 factory.loadProviders();
574 Instantiator instantiator = factory.getInstantiator(element.getMimeType().getBaseMimeMediaType());
576 if ((instantiator instanceof TextInstantiator) && (element instanceof TextMessageElement)) {
577 return ((TextInstantiator) instantiator).newInstance(element.getMimeType(), ((TextMessageElement) element).getReader());
579 return instantiator.newInstance(element.getMimeType(), element.getStream());