package com.metsci.glimpse.examples.webstart; import java.awt.BorderLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.BufferedReader; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.lang.reflect.Method; import java.net.JarURLConnection; import java.net.URL; import java.net.URLConnection; import java.util.ArrayList; import java.util.Collection; import java.util.Enumeration; import java.util.List; import java.util.Map; import java.util.TreeMap; import java.util.jar.JarEntry; import java.util.jar.JarFile; import javax.swing.JButton; import javax.swing.JEditorPane; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JSplitPane; import javax.swing.JTree; import javax.swing.SwingUtilities; import javax.swing.SwingWorker; import javax.swing.event.TreeSelectionEvent; import javax.swing.event.TreeSelectionListener; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.DefaultTreeModel; import javax.swing.tree.TreeModel; import javax.swing.tree.TreePath; import javax.swing.tree.TreeSelectionModel; import jsyntaxpane.DefaultSyntaxKit; import com.metsci.glimpse.examples.Example; @SuppressWarnings( "serial" ) public class ExampleRunner extends JSplitPane { private JTree exampleTree; private JEditorPane codeArea; private JButton runExampleButton; private Class<?> exampleClass; public ExampleRunner( ) { // initialize jsyntaxpane syntax highlighting DefaultSyntaxKit.initKit(); setOrientation( JSplitPane.HORIZONTAL_SPLIT ); exampleTree = new JTree( new DefaultTreeModel( new DefaultMutableTreeNode( "Loading ..." ) ) ); codeArea = new JEditorPane( ); JScrollPane scrollPane = new JScrollPane( codeArea ); codeArea.setContentType("text/java"); codeArea.setEditable( false ); runExampleButton = new JButton( "Run Example" ); runExampleButton.setEnabled( false ); exampleTree.getSelectionModel( ).setSelectionMode( TreeSelectionModel.SINGLE_TREE_SELECTION ); exampleTree.addTreeSelectionListener( new TreeSelectionListener( ) { @Override public void valueChanged( TreeSelectionEvent e ) { if ( exampleTree.getSelectionCount( ) > 0 ) { TreePath selectionPath = exampleTree.getSelectionPath( ); DefaultMutableTreeNode node = ( DefaultMutableTreeNode ) selectionPath.getLastPathComponent( ); if ( node.getUserObject( ) instanceof Class<?> ) { Class<?> clazz = ( Class<?> ) node.getUserObject( ); selectClass( clazz ); } else { selectClass( null ); } } } } ); runExampleButton.addActionListener( new ActionListener( ) { @Override public void actionPerformed( ActionEvent e ) { runExample( exampleClass ); } } ); JPanel rightPanel = new JPanel( new BorderLayout( ) ); rightPanel.add( scrollPane, BorderLayout.CENTER ); JPanel buttonPanel = new JPanel( new BorderLayout( ) ); buttonPanel.add( runExampleButton, BorderLayout.WEST ); rightPanel.add( buttonPanel, BorderLayout.SOUTH ); setLeftComponent( new JScrollPane( exampleTree ) ); setRightComponent( rightPanel ); setDividerLocation( 300 ); codeArea.setContentType( "text/java" ); } public void populateExamples( ) { new SwingWorker<Collection<Class<?>>, Void>( ) { @Override protected Collection<Class<?>> doInBackground( ) throws Exception { return getExamples( ); } @Override protected void done( ) { try { Collection<Class<?>> classes = get( ); exampleTree.setModel( createTreeModel( classes ) ); } catch ( Exception e ) { JOptionPane.showMessageDialog( SwingUtilities.getWindowAncestor( ExampleRunner.this ), e.getMessage( ), "Error!", JOptionPane.ERROR_MESSAGE ); } } }.execute( ); } private void runExample( final Class<?> clazz ) { new SwingWorker<Void, Void>( ) { @Override protected Void doInBackground( ) throws Exception { Method mainMethod = exampleClass.getMethod( "main", String[].class ); mainMethod.invoke( null, new Object[] { new String[0] } ); return null; } @Override protected void done( ) { try { get( ); } catch ( Exception e ) { JOptionPane.showMessageDialog( SwingUtilities.getWindowAncestor( ExampleRunner.this ), e.getMessage( ), "Error!", JOptionPane.ERROR_MESSAGE ); } } }.execute( ); } private TreeModel createTreeModel( Collection<Class<?>> examples ) { Package rootPackage = Package.getPackage( "com.metsci.glimpse.examples" ); SimpleNode root = new SimpleNode( rootPackage ); Map<String, SimpleNode> packageNodes = new TreeMap<String, SimpleNode>( ); packageNodes.put( rootPackage.getName( ), root ); for ( Class<?> example : examples ) { Package p = example.getPackage( ); SimpleNode packageNode = getPackageNode( p, packageNodes ); packageNode.add( new SimpleNode( example ) ); } return new DefaultTreeModel( root ); } private SimpleNode getPackageNode( Package p, Map<String, SimpleNode> packageNodes ) { SimpleNode node = packageNodes.get( p.getName( ) ); if ( node == null ) { Package pp = getParentPackage( p ); SimpleNode parentNode = getPackageNode( pp, packageNodes ); node = new SimpleNode( p ); parentNode.add( node ); packageNodes.put( p.getName( ), node ); } return node; } private Package getParentPackage( Package child ) { String name = child.getName( ); String parentName = name.substring( 0, name.lastIndexOf( '.' ) ); return Package.getPackage( parentName ); } private void selectClass( final Class<?> clazz ) { runExampleButton.setEnabled( false ); if ( clazz == null ) { codeArea.setText( "" ); return; } new SwingWorker<String, Void>( ) { @Override protected String doInBackground( ) throws Exception { return getSource( clazz ); } @Override protected void done( ) { try { codeArea.setText( get( ) ); // scroll to top codeArea.setCaretPosition( 0 ); exampleClass = clazz; runExampleButton.setEnabled( true ); } catch ( Exception e ) { JOptionPane.showMessageDialog( SwingUtilities.getWindowAncestor( ExampleRunner.this ), e.getMessage( ), "Error!", JOptionPane.ERROR_MESSAGE ); } } }.execute( ); } private String getSource( Class<?> clazz ) throws IOException { String file = clazz.getName( ).replace( '.', '/' ) + ".java"; String eol = System.getProperty( "line.separator" ); StringBuilder builder = new StringBuilder( ); String line = null; InputStream in = Example.class.getClassLoader( ).getResourceAsStream( file ); if ( in == null ) { throw new FileNotFoundException( "Source file not found for " + clazz.getName( ) ); } BufferedReader reader = new BufferedReader( new InputStreamReader( in ) ); while ( ( line = reader.readLine( ) ) != null ) { builder.append( line ); builder.append( eol ); } reader.close( ); return builder.toString( ); } private Collection<Class<?>> getExamples( ) throws ClassNotFoundException, IOException { List<Class<?>> exampleClasses = new ArrayList<Class<?>>( ); URL url = Example.class.getResource( Example.class.getSimpleName( ) + ".class" ); URLConnection connection = url.openConnection( ); if ( connection instanceof JarURLConnection ) { JarFile jar = ( ( JarURLConnection ) connection ).getJarFile( ); Enumeration<JarEntry> entries = jar.entries( ); while ( entries.hasMoreElements( ) ) { JarEntry entry = entries.nextElement( ); String name = entry.getName( ); if ( name.endsWith( "Example.class" ) ) { String className = name.replace( '/', '.' ).substring( 0, name.length( ) - ".class".length( ) ); Class<?> clazz = Class.forName( className ); // special case if ( clazz.getSimpleName( ).equals( "Example" ) ) { continue; } else { exampleClasses.add( clazz ); } } } } else { throw new UnsupportedOperationException( "Must run as jar to use JarFileConnection" ); } return exampleClasses; } private static class SimpleNode extends DefaultMutableTreeNode { SimpleNode( Object object ) { super( object ); } @Override public String toString( ) { Object o = getUserObject( ); if ( o instanceof Package ) { String name = ( ( Package ) o ).getName( ); return name.substring( name.lastIndexOf( '.' ) + 1 ); } else if ( o instanceof Class ) { return ( ( Class<?> ) o ).getSimpleName( ); } else { return String.valueOf( o ); } } } }