package net.sf.fmj.registry;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.Writer;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;
import java.util.logging.Logger;
import javax.media.CaptureDeviceInfo;
import javax.media.MediaLocator;
import javax.media.PlugInManager;
import net.sf.fmj.utility.LoggerSingleton;
import net.sf.fmj.utility.SerializationUtils;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
import org.jdom.output.Format;
import org.jdom.output.XMLOutputter;
/**
* Implementation of RegistryIO using XML.
* @author Ken Larson
* @author Warren Bloomer
*
*/
class XMLRegistryIO implements RegistryIO
{
private static final Logger logger = LoggerSingleton.logger;
private final RegistryContents registryContents;
/** version of the registry file format */
private static final String version = "0.1";
public XMLRegistryIO(RegistryContents registryContents)
{
super();
this.registryContents = registryContents;
}
/* ---------------------- XML operations --------------------- */
private static final String ELEMENT_REGISTRY = "registry";
private static final String ATTR_VERSION = "version";
private static final String ELEMENT_PROTO_PREFIX = "protocol-prefixes";
private static final String ELEMENT_CONTENT_PREFIX = "content-prefixes";
private static final String ELEMENT_PLUGINS = "plugins";
private static final String ELEMENT_MIMETYPES = "mime-types";
private static final String ELEMENT_MIMETYPE = "type";
private static final String ELEMENT_CAPTURE_DEVICES = "capture-devices";
private static final String ELEMENT_CODECS = "codecs";
private static final String ELEMENT_DEMUXES = "demuxes";
private static final String ELEMENT_MUXES = "muxes";
private static final String ELEMENT_EFFECTS = "effects";
private static final String ELEMENT_RENDERERS = "renderers";
private static final String ELEMENT_PREFIX = "prefix";
private static final String ELEMENT_CLASS = "class";
private static final String ELEMENT_DEVICE = "device";
private static final String ELEMENT_DEVICE_NAME = "name";
private static final String ELEMENT_DEVICE_LOCATOR = "locator";
private static final String ELEMENT_DEVICE_FORMAT = "format";
private static final String ELEMENT_DEVICE_FORMAT_CLASS = "class";
private static final String ELEMENT_DEVICE_FORMAT_DESCRIPTION = "description";
private static final String ELEMENT_DEVICE_FORMAT_SERIALIZED = "serialized";
public void load(InputStream is) throws IOException
{
load(new InputStreamReader(is));
}
/**
* Load the Registry data from a Reader/
*/
public void load(Reader reader) throws IOException {
// read the registry
try {
SAXBuilder saxBuilder = new SAXBuilder();
Document document = saxBuilder.build(reader);
// read all the data from the document and place into data structures.
loadDocument(document);
}
catch (JDOMException e) {
// problem parsing XML.
throw new IOException(e.getMessage());
}
}
public void write(OutputStream os) throws IOException {
write (new OutputStreamWriter(os));
}
/**
* Write the registry data to the Writer.
*
* @param writer destination for the registry data.
* @throws IOException
*/
public void write(Writer writer) throws IOException {
// build document from registry data structures
Document document = buildDocument();
XMLOutputter xmlOutputter = new XMLOutputter();
xmlOutputter.setFormat(Format.getPrettyFormat());
xmlOutputter.output(document, writer);
}
private void loadDocument(Document document) throws IOException {
Element rootElement = document.getRootElement();
String versionString = rootElement.getAttributeValue(ATTR_VERSION);
// TODO use version String
logger.info("FMJ registry document version " + versionString);
Element pluginsElement = rootElement.getChild(ELEMENT_PLUGINS);
loadPlugins(pluginsElement);
Element contentPrefixesElement = rootElement.getChild(ELEMENT_CONTENT_PREFIX);
loadContentPrefixes(contentPrefixesElement);
Element protocolPrefixesElement = rootElement.getChild(ELEMENT_PROTO_PREFIX);
loadProtocolPrefixes(protocolPrefixesElement);
Element mimetypesElement = rootElement.getChild(ELEMENT_MIMETYPES);
loadMimeTypes(mimetypesElement);
// load capture devices
Element captureDevicesElement = rootElement.getChild(ELEMENT_CAPTURE_DEVICES);
try {
loadCaptureDevices(captureDevicesElement);
} catch (ClassNotFoundException e) {
throw new IOException(e.getMessage());
}
}
private void loadPlugins(Element element) {
Element codecsElement = element.getChild(ELEMENT_CODECS);
loadPlugins(codecsElement, PlugInManager.CODEC);
Element effectsElement = element.getChild(ELEMENT_EFFECTS);
loadPlugins(effectsElement, PlugInManager.EFFECT);
Element renderersElement = element.getChild(ELEMENT_RENDERERS);
loadPlugins(renderersElement, PlugInManager.RENDERER);
Element muxesElement = element.getChild(ELEMENT_MUXES);
loadPlugins(muxesElement, PlugInManager.MULTIPLEXER);
Element demuxesElement = element.getChild(ELEMENT_DEMUXES);
loadPlugins(demuxesElement, PlugInManager.DEMULTIPLEXER);
}
/**
*
* @param type
*/
private void loadPlugins(Element element, int type) {
if (element == null) {
return;
}
Vector vector = registryContents.plugins[type-1];
List pluginElements = element.getChildren(ELEMENT_CLASS);
for (int i=0; i<pluginElements.size(); i++) {
Element pluginElement = (Element) pluginElements.get(i);
String classname = pluginElement.getTextTrim();
vector.add(classname);
}
}
private void loadProtocolPrefixes(Element element) {
registryContents.protocolPrefixList.clear();
List list = element.getChildren(ELEMENT_PREFIX);
for (int i=0; i<list.size(); i++) {
Element prefixElement = (Element) list.get(i);
registryContents.protocolPrefixList.add(prefixElement.getTextTrim());
}
}
private void loadContentPrefixes(Element element) {
registryContents.contentPrefixList.clear();
List list = element.getChildren(ELEMENT_PREFIX);
for (int i=0; i<list.size(); i++) {
Element prefixElement = (Element) list.get(i);
registryContents.contentPrefixList.add(prefixElement.getTextTrim());
}
}
/**
* Builds a Document from the registry data structures.
*
* @return the Document.
* @throws IOException
*/
private Document buildDocument() throws IOException {
Document document = new Document();
Element rootElement = new Element(ELEMENT_REGISTRY);
rootElement.setAttribute(ATTR_VERSION, version);
document.setRootElement(rootElement);
rootElement.addContent(getPluginsElement());
rootElement.addContent(getContentElement());
rootElement.addContent(getProtocolElement());
rootElement.addContent(getMimeElement());
rootElement.addContent(getCaptureDeviceElement());
return document;
}
private Element getPluginsElement() {
Element pluginElement = new Element(ELEMENT_PLUGINS);
pluginElement.addContent(getCodecElement());
pluginElement.addContent(getDemuxElement());
pluginElement.addContent(getEffectElement());
pluginElement.addContent(getMuxElement());
pluginElement.addContent(getRendererElement());
return pluginElement;
}
private Element getCodecElement() {
return getPluginElement(PlugInManager.CODEC, ELEMENT_CODECS);
}
private Element getDemuxElement() {
return getPluginElement(PlugInManager.DEMULTIPLEXER, ELEMENT_DEMUXES);
}
private Element getEffectElement() {
return getPluginElement(PlugInManager.EFFECT, ELEMENT_EFFECTS);
}
private Element getMuxElement() {
return getPluginElement(PlugInManager.MULTIPLEXER, ELEMENT_MUXES);
}
private Element getRendererElement() {
return getPluginElement(PlugInManager.RENDERER, ELEMENT_RENDERERS);
}
private Element getPluginElement(int pluginType, String typeName) {
Element pluginsElement = new Element(typeName);
Vector plugins = registryContents.plugins[pluginType-1];
if (plugins != null) {
Iterator pluginIter = plugins.iterator();
while (pluginIter.hasNext()) {
String classname = (String) pluginIter.next();
Element pluginElement = new Element(ELEMENT_CLASS);
pluginElement.setText(classname);
pluginsElement.addContent(pluginElement);
}
}
return pluginsElement;
}
private Element getContentElement() {
Element contentElement = new Element(ELEMENT_CONTENT_PREFIX);
Iterator prefixIter = registryContents.contentPrefixList.iterator();
while (prefixIter.hasNext()) {
String prefix = (String) prefixIter.next();
Element prefixElement = new Element(ELEMENT_PREFIX);
prefixElement.setText(prefix);
contentElement.addContent(prefixElement);
}
return contentElement;
}
private Element getProtocolElement() {
Element protocolElement = new Element(ELEMENT_PROTO_PREFIX);
Iterator prefixIter = registryContents.protocolPrefixList.iterator();
while (prefixIter.hasNext()) {
String prefix = (String) prefixIter.next();
Element prefixElement = new Element(ELEMENT_PREFIX);
prefixElement.setText(prefix);
protocolElement.addContent(prefixElement);
}
return protocolElement;
}
/* -------------- MIME methods -------------- */
private void loadMimeTypes(Element element)
{
registryContents.mimeTable.clear();
final List list = element.getChildren(ELEMENT_MIMETYPE);
for (int i=0; i<list.size(); i++) {
final Element typeElement = (Element) list.get(i);
String type = typeElement.getAttributeValue("value");
String defaultExtension = typeElement.getAttributeValue("default-ext");
final List list2 = typeElement.getChildren("ext");
for (int j=0; j<list2.size(); j++) {
final Element extElement = (Element) list2.get(j);
String ext = extElement.getText();
registryContents.mimeTable.addMimeType(ext, type);
}
registryContents.mimeTable.addMimeType(defaultExtension, type);
}
}
private Element getMimeElement() {
Element mimeElement = new Element(ELEMENT_MIMETYPES);
final Iterator typesIterator = registryContents.mimeTable.getMimeTypes().iterator();
while (typesIterator.hasNext())
{
final String type = (String) typesIterator.next();
final List extensions = registryContents.mimeTable.getExtensions(type);
final Element typeElement = new Element(ELEMENT_MIMETYPE);
typeElement.setAttribute("value", type);
typeElement.setAttribute("default-ext", registryContents.mimeTable.getDefaultExtension(type));
mimeElement.addContent(typeElement);
for (int i = 0; i < extensions.size(); ++i)
{
String ext = (String) extensions.get(i);
final Element extElement = new Element("ext");
extElement.setText(ext);
typeElement.addContent(extElement);
}
}
return mimeElement;
}
/* -------------- Capture Device methods -------------- */
private void loadCaptureDevices(Element element) throws IOException, ClassNotFoundException {
registryContents.captureDeviceInfoList.clear();
final List list = element.getChildren(ELEMENT_DEVICE);
for (int i=0; i<list.size(); i++) {
final Element deviceElement = (Element) list.get(i);
final Element deviceNameElement = deviceElement.getChild(ELEMENT_DEVICE_NAME);
final Element deviceLocatorElement = deviceElement.getChild(ELEMENT_DEVICE_LOCATOR);
final List formatElementsList = deviceElement.getChildren(ELEMENT_DEVICE_FORMAT);
final javax.media.Format[] formats = new javax.media.Format[formatElementsList.size()];
for (int j = 0; j < formatElementsList.size(); ++j) {
final Element formatElement = (Element) formatElementsList.get(j);
final Element serializedElement = formatElement.getChild(ELEMENT_DEVICE_FORMAT_SERIALIZED);
formats[j] = SerializationUtils.deserialize(serializedElement.getTextTrim());
}
final CaptureDeviceInfo info = new CaptureDeviceInfo(deviceNameElement.getTextTrim(), new MediaLocator(deviceLocatorElement.getTextTrim()), formats);
registryContents.captureDeviceInfoList.add(info);
}
}
private Element getCaptureDeviceElement() throws IOException {
final Element captureDeviceElement = new Element(ELEMENT_CAPTURE_DEVICES);
final Iterator<CaptureDeviceInfo> iter = registryContents.captureDeviceInfoList.iterator();
while (iter.hasNext()) {
final CaptureDeviceInfo info = iter.next();
if (info.getLocator() == null)
continue; // should never be null, only seems to be null due to some unit tests.
final Element deviceElement = new Element(ELEMENT_DEVICE);
{
final Element deviceNameElement = new Element(ELEMENT_DEVICE_NAME);
deviceNameElement.setText(info.getName());
deviceElement.addContent(deviceNameElement);
}
{
final Element e = new Element(ELEMENT_DEVICE_LOCATOR);
e.setText(info.getLocator().toExternalForm());
deviceElement.addContent(e);
}
{
final javax.media.Format[] formats = info.getFormats();
for (int i = 0; i < formats.length; ++i)
{
final Element formatElement = new Element(ELEMENT_DEVICE_FORMAT);
{
final Element e2 = new Element(ELEMENT_DEVICE_FORMAT_CLASS); // for XML readability only
e2.setText(formats[i].getClass().getName());
formatElement.addContent(e2);
}
{
final Element e2 = new Element(ELEMENT_DEVICE_FORMAT_DESCRIPTION); // for XML readability only
e2.setText(formats[i].toString());
formatElement.addContent(e2);
}
// TODO: perhaps "known" formats like RGBFormat could be serialized much more nicely.
// we have to use serialization because that is the only way to support JMF-compatible subclasses
// that are not in JMF.
{
final Element e2 = new Element(ELEMENT_DEVICE_FORMAT_SERIALIZED);
e2.setText(SerializationUtils.serialize(formats[i]));
formatElement.addContent(e2);
}
deviceElement.addContent(formatElement);
}
}
captureDeviceElement.addContent(deviceElement);
}
return captureDeviceElement;
}
}