6_-_Creating_a_viewer

Allan Cunliffe

To display the Foo format, we will take the data and put each 'part' into a cell in a table. In this way, the Foo file will be rendered as it would appear in one of Organization X's software applications. Later we may attempt to extend the Foo viewer to perform different actions for different tags, but since the Foo normaliser at the moment simply creates the <foo:part> we will simply stick the content of each part tag into it's own cell.

To create a Viewer to plug into Xena, we need to extend the XenaViewer class, from the au.gov.naa.digipres.xena.kernel.view package. This class extends the Swing JPanel object and implements the cloneable interface - this is required for the way Xena renders a file.

When XenaView objects are loaded by the ViewManager, the ViewManager will instantiate an instance of the class and store it in a list. When Xena wants to view a normalised data object, the ViewManager object opens the file, and tries to find the appropriate viewer to use by looking at the opening tag. When the appropriate view is found, Xena will clone the view, and return it, so as to allow the view to parse the input and render it appropriately. Some views, such as those for meta data wrappers, require sub-views. In this case the view will pass the sub view some data, and when finished will continue to render the rest of the view itself.

Create FooViewer

So now to create our FooViewer. This object extends XenaView, and since it is a concrete class, it must implement the abstract methods.

package au.gov.naa.digipres.xena.demo.foo;

import au.gov.naa.digipres.xena.kernel.XenaException;
import au.gov.naa.digipres.xena.kernel.view.XenaView;

public class FooViewer extends XenaView {

    @Override
    public String getViewName() {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public boolean canShowTag(String tag) throws XenaException {
        // TODO Auto-generated method stub
        return false;
    }

}

So, these methods are not too hard to implement. Let's do them both together.

    @Override
    public String getViewName() {
        return "Foo view";
    }

    @Override
    public boolean canShowTag(String tag) throws XenaException {
        return FooNormaliser.FOO_OPENING_ELEMENT_QUALIFIED_NAME.equals(tag);
    }

Implement the view

So now we have done this, it is time to implement the view itself. Since we will be using a table, it is time to create this table in our view, and prepare it to accept the data:

package au.gov.naa.digipres.xena.demo.foo;

import java.awt.Dimension;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Vector;

import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.AbstractTableModel;

import au.gov.naa.digipres.xena.kernel.view.XenaView;

public class FooViewer extends XenaView {

        private JTable fooPartTable;
        private FooPartTableModel tableModel;
        private JScrollPane scrollPane;

        public FooViewer() {
                super();
                tableModel = new FooPartTableModel();
                fooPartTable = new JTable(tableModel);
                scrollPane = new JScrollPane(fooPartTable);
                fooPartTable.setPreferredScrollableViewportSize(new Dimension(500,70));
                add(scrollPane);
        }

        public String getViewName() {
                return "Foo view";
        }

        public boolean canShowTag(String tag) {
                return FooNormaliser.FOO_OPENING_ELEMENT_QUALIFIED_NAME.equals(tag);
        }

        private static class FooPartTableModel extends AbstractTableModel {
                private static int PART = 0;
                private static int DATA = 1;

                private static String[] columnNames = {"Part", "Data"};
                private static Class&lt;Object[]&gt; tableEntries = new Vector&lt;Object[]&gt;();

                public void setEntries(List&lt;String&gt; fooParts) {
                        tableEntries.clear();
                if (fooParts != null) {
                                Iterator&lt;String&gt; it = fooParts.iterator();
                                int i = 1;
                                while (it.hasNext()) {
                                        Object[] result = new Object[getColumnCount()];
                                        result[PART] = new Integer(i++);
                                        result[DATA] = it.next();
                                        tableEntries.add(result);
                                }
                        }
                        fireTableDataChanged();
                }

                public int getRowCount() {
                        return tableEntries.size();
                }

                public Object getValueAt(int row, int col) {
                        Object[] dataObject = (Object[]) tableEntries.get(row);
                        return dataObject[col];
                }

                public int getColumnCount() {
                        return columnNames.length;
                }

                public Class

Write the content handler

The content handler will be invoked to parse the XML. The abstract XenaViewer class has a method getContentHandler(), that if not over ridden will simply return an empty content handler. Wee need to override it to do something useful. We will parse the XML and put everything within the <foo:part> tags into the table a new table row, along with a count. To do this we will add a method to the FooTableModel addFooPart():

    @Override
    public ContentHandler getContentHandler() {
        return new XMLFilterImpl() {
            private StringBuffer fooContent;

            @Override
            public void startElement (String uri, String localName, String qName, Attributes atts) {
                                if (qName.equals(FooNormaliser.FOO_PART_ELEMENT_QUALIFIED_NAME)) {
                                        fooContent = new StringBuffer();
                                }
                        }

                        @Override
                        public void characters(char[] ch, int start, int length) throws SAXException {
                                fooContent.append(ch, start, length);
                        }

                        @Override
                        public void endElement(String uri, String localName, String qName) {
                                if (qName.equals(FooNormaliser.FOO_PART_ELEMENT_QUALIFIED_NAME)) {
                                        tableModel.addFooPart(new String(fooContent));
                                }
                        }
                };
        }

        private static class FooPartTableModel extends AbstractTableModel {
                private static int PART = 0;
                private static int DATA = 1;

                private static String[] columnNames = {"Part", "Data"};
                private static Class&lt;Object[]&gt; tableEntries = new Vector&lt;Object[]&gt;();

                public void setEntries(List&lt;String&gt; fooParts) {
                        tableEntries.clear();
                if (fooParts != null) {
                                Iterator&lt;String&gt; it = fooParts.iterator();
                                int i = 1;
                                while (it.hasNext()) {
                                        Object[] result = new Object[getColumnCount()];
                                        result[PART] = new Integer(i++);
                                        result[DATA] = it.next();
                                        tableEntries.add(result);
                                }
                        }
                        fireTableDataChanged();
                }

                public int getRowCount() {
                        return tableEntries.size();
                }

                public Object getValueAt(int row, int col) {
                        Object[] dataObject = (Object[]) tableEntries.get(row);
                        return dataObject[col];
                }

                public int getColumnCount() {
                        return columnNames.length;
                }

                public Class

In order to load the viewer we need to implement the getViews method in the FooPlugin.java file:

@Override
public List&lt;XenaView&gt; getViews() {
        List&lt;XenaView&gt; viewList = new ArrayList&lt;XenaView&gt;();
        viewList.add(new FooViewer());
        return viewList;
}

Test the viewer

Create a test harness to ensure our viewer works. This is fairly straightforward, the major steps are as follows:

1. First we create a Xena object.

2. Load the Foo plugin.

3. Create a NormalisedObjectViewFactory object that will return an appropriate view.

4. Create a frame to put our view in.

5. Create the file object that we wish to view.

6. Get our view from the NormalisedObjectViewFactory object.

7. Pack our view in the frame and display it.

Since we are using the default Xena package wrapper and file namer, the file that was produced using our normaliser test harness (the NormaliseTester class) will be named in the format [original filename][normaliser name].xena_, where the:

  • original file that we normalised was called example_file.foo
  • normaliser was called Foo
  • normalised file to be displayed will be called example_file.foo_Foo.xena.

Also, it will have the default Xena metadata wrapped around the Foo content, and this will appear in the view created. The code below refers to an example of the normalised file which has been placed in the data directory. If you want to use your own normalised file, you will need to modify the path to the viewFile in the code below.

package au.gov.naa.digipres.xena.demo.foo.test;

import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.util.List;
import java.util.Vector;
import javax.swing.JFrame;
import javax.swing.JPanel;
import au.gov.naa.digipres.xena.core.NormalisedObjectViewFactory;
import au.gov.naa.digipres.xena.core.Xena;
import au.gov.naa.digipres.xena.kernel.XenaException;
import au.gov.naa.digipres.xena.kernel.view.XenaView;

public class ViewerTester {
        public static void main(String[] argv) {
                //create a Xena object
                Xena xena = new Xena();

                //load the foo plugin; our foo jar will already be on the class path, so we can load it by name.
                try {
                        xena.loadPlugin("au/gov/naa/digipres/xena/demo/foo");
                } catch (XenaException xe) {
                        xe.printStackTrace();
                        return;
                }

                //create the view factory
                NormalisedObjectViewFactory novf = new NormalisedObjectViewFactory(xena);

                //create our frame
                JFrame frame = new JFrame("XenaTester View");
                frame.addWindowListener(new WindowAdapter() {
                        public void windowClosing(WindowEvent e){
                                System.exit(0);
                        }
                });

                //create our view file
                File viewFile = new File("../../../data/example_file.foo_Foo.xena");

                //get our view
                JPanel view = null;
                try {
                        view = novf.getView(viewFile, null);
                } catch (XenaException e){
                        e.printStackTrace();
                }

                //add it to our frame and display it!
                frame.setBounds(200,250,300,200);
                frame.getContentPane().add(view);
                frame.pack();
                frame.setVisible(true);
        }
}

Related

Wiki: Main_Page