All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.metsci.glimpse.examples.dnc.Vpf2FlatConverter Maven / Gradle / Ivy

/*
 * Copyright (c) 2016, Metron, Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of Metron, Inc. nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL METRON, INC. BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package com.metsci.glimpse.examples.dnc;

import static com.google.common.base.Charsets.*;
import static com.metsci.glimpse.dnc.DncDataPaths.*;
import static com.metsci.glimpse.dnc.convert.Vpf.*;
import static com.metsci.glimpse.dnc.convert.Vpf2Flat.*;
import static com.metsci.glimpse.dnc.util.DncMiscUtils.*;
import static com.metsci.glimpse.docking.DockingUtils.*;
import static com.metsci.glimpse.examples.dnc.DncExampleUtils.*;
import static com.metsci.glimpse.platformFixes.PlatformFixes.*;
import static com.metsci.glimpse.tinylaf.TinyLafUtils.*;
import static com.metsci.glimpse.util.logging.LoggerUtils.*;
import static javax.swing.JFileChooser.*;
import static javax.swing.JOptionPane.*;
import static javax.swing.ScrollPaneConstants.*;
import static javax.swing.WindowConstants.*;

import java.awt.Component;
import java.awt.Dimension;
import java.io.File;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.atomic.AtomicBoolean;

import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;

import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.metsci.glimpse.dnc.convert.Vpf2Flat.Database;
import com.metsci.glimpse.dnc.util.DncMiscUtils.ThrowingRunnable;
import com.metsci.glimpse.dnc.util.SingletonEvictingBlockingQueue;

import net.miginfocom.swing.MigLayout;

public class Vpf2FlatConverter
{

    public static void main( String[] args )
    {
        initializeLogging( "dnc-examples/logging.properties" );
        fixPlatformQuirks( );
        initTinyLaf( );


        SwingUtilities.invokeLater( ( ) ->
        {

            // Shared state
            //

            class State
            {
                public File browseDir = new File( System.getProperty( "user.home" ) );
                public Map dbDirs = new LinkedHashMap<>( );
                public Set dbNames = new LinkedHashSet<>( );
            }
            State state = new State( );


            // Components
            //

            JTextField vpfParentField = new JTextField( 32 );
            vpfParentField.setText( "" );

            JButton vpfParentButton = new JButton( requireIcon( "icons/fugue/folder-horizontal-open.png" ) );
            vpfParentButton.setToolTipText( "Browse" );

            JPanel vpfCheckboxesPanel = new JPanel( new MigLayout( "insets 2 4 2 2, gapy 0, wrap 1" ) );

            JScrollPane vpfCheckboxesScroller = new JScrollPane( vpfCheckboxesPanel, VERTICAL_SCROLLBAR_ALWAYS, HORIZONTAL_SCROLLBAR_NEVER );
            int vpfCheckboxHeight = ( new JCheckBox( "X" ) ).getPreferredSize( ).height;
            vpfCheckboxesScroller.getVerticalScrollBar( ).setUnitIncrement( vpfCheckboxHeight );
            vpfCheckboxesScroller.setPreferredSize( new Dimension( 100, 5*vpfCheckboxHeight + 4 ) );

            JButton vpfCheckAllButton = new JButton( requireIcon( "icons/fugue/ui-check-box.png" ) );
            vpfCheckAllButton.setToolTipText( "Select All" );

            JButton vpfCheckNoneButton = new JButton( requireIcon( "icons/fugue/ui-check-box-uncheck.png" ) );
            vpfCheckNoneButton.setToolTipText( "Select None" );

            JTextField flatParentField = new JTextField( 32 );
            flatParentField.setText( glimpseDncDefaultUserFlatDir.getAbsolutePath( ) );

            JButton flatParentButton = new JButton( requireIcon( "icons/fugue/folder-horizontal-open.png" ) );
            flatParentButton.setToolTipText( "Browse" );

            JButton convertButton = new JButton( "Convert" );


            // Layout
            //

            JPanel contentPane = new JPanel( new MigLayout( ) );

            contentPane.add( new JLabel( "From:" ), "wrap" );
            contentPane.add( new JLabel( "VPF Parent:" ), "gapleft 8" );
            contentPane.add( vpfParentField, "growx, pushx" );
            contentPane.add( vpfParentButton, "wrap" );

            contentPane.add( new JLabel( "Databases:" ), "gapleft 8" );
            contentPane.add( vpfCheckboxesScroller, "grow, span 1 3" );
            contentPane.add( vpfCheckAllButton, "wrap" );
            contentPane.add( vpfCheckNoneButton, "skip 1, wrap" );
            contentPane.add( new JPanel( ), "pushy, wrap" );

            contentPane.add( new JLabel( "To:" ), "gapy 12, wrap" );
            contentPane.add( new JLabel( "FLAT Parent:" ), "gapleft 8" );
            contentPane.add( flatParentField, "growx, pushx" );
            contentPane.add( flatParentButton, "wrap" );

            contentPane.add( convertButton, "gapy 12, span, split, alignx right, wrap" );

            JFrame frame = new JFrame( "DNC Converter" );
            frame.setContentPane( contentPane );


            // Browse to VPF dir
            //

            vpfParentButton.addActionListener( ( ev ) ->
            {
                JFileChooser chooser = new JFileChooser( );
                chooser.setApproveButtonText( "Select" );
                chooser.setCurrentDirectory( state.browseDir );
                chooser.setDialogTitle( "Select VPF Parent" );
                chooser.setFileSelectionMode( DIRECTORIES_ONLY );
                chooser.setMultiSelectionEnabled( false );
                chooser.setAcceptAllFileFilterUsed( false );

                if ( chooser.showOpenDialog( contentPane ) == APPROVE_OPTION )
                {
                    File dir = chooser.getSelectedFile( );
                    vpfParentField.setText( dir.getPath( ) );

                    state.browseDir = dir;
                }
            } );


            // Load VPF databases list
            //

            BlockingQueue vpfParentPaths = new SingletonEvictingBlockingQueue<>( );

            addTextListener( vpfParentField, ( ) ->
            {
                vpfParentPaths.add( vpfParentField.getText( ) );
            } );

            startThread( "VPF Database Lister", true, new ThrowingRunnable( )
            {
                String oldPath = null;
                public void runThrows( ) throws Exception
                {
                    while ( true )
                    {
                        String newPath = takeNewValue( vpfParentPaths, oldPath );

                        SwingUtilities.invokeAndWait( ( ) ->
                        {
                            vpfCheckboxesPanel.removeAll( );
                            vpfCheckboxesScroller.validate( );
                        } );

                        Map dbDirs = vpfDatabaseDirsByName( new File( newPath ) );

                        SwingUtilities.invokeAndWait( ( ) ->
                        {
                            state.dbDirs = dbDirs;
                            state.dbNames = new LinkedHashSet<>( dbDirs.keySet( ) );

                            dbDirs.forEach( ( dbName, dbDir ) ->
                            {
                                JCheckBox checkbox = new JCheckBox( dbName );
                                checkbox.addItemListener( ( ev ) ->
                                {
                                    if ( checkbox.isSelected( ) )
                                    {
                                        state.dbNames.add( dbName );
                                    }
                                    else
                                    {
                                        state.dbNames.remove( dbName );
                                    }
                                } );
                                checkbox.setSelected( true );
                                vpfCheckboxesPanel.add( checkbox );
                            } );
                            vpfCheckboxesScroller.validate( );
                        } );

                        oldPath = newPath;
                    }
                }
            } );


            // Select all checkboxes
            //

            vpfCheckAllButton.addActionListener( ( ev ) ->
            {
                for ( Component c : vpfCheckboxesPanel.getComponents( ) )
                {
                    if ( c instanceof JCheckBox )
                    {
                        ( ( JCheckBox ) c ).setSelected( true );
                    }
                }
            } );


            // De-select all checkboxes
            //

            vpfCheckNoneButton.addActionListener( ( ev ) ->
            {
                for ( Component c : vpfCheckboxesPanel.getComponents( ) )
                {
                    if ( c instanceof JCheckBox )
                    {
                        ( ( JCheckBox ) c ).setSelected( false );
                    }
                }
            } );


            // Browse to FLAT dir
            //

            flatParentButton.addActionListener( ( ev ) ->
            {
                JFileChooser chooser = new JFileChooser( );
                chooser.setApproveButtonText( "Select" );
                chooser.setCurrentDirectory( state.browseDir );
                chooser.setDialogTitle( "Select FLAT Parent" );
                chooser.setFileSelectionMode( DIRECTORIES_ONLY );
                chooser.setMultiSelectionEnabled( false );
                chooser.setAcceptAllFileFilterUsed( false );

                if ( chooser.showSaveDialog( contentPane ) == APPROVE_OPTION )
                {
                    File dir = chooser.getSelectedFile( );
                    flatParentField.setText( dir.getPath( ) );

                    state.browseDir = dir;
                }
            } );


            // Convert button
            //

            convertButton.addActionListener( ( ev ) ->
            {
                File flatParentDir = new File( flatParentField.getText( ) );
                Map dbDirs = ImmutableMap.copyOf( state.dbDirs );
                Set dbNames = ImmutableSet.copyOf( state.dbNames );

                setTreeEnabled( contentPane, false );

                JFrame progressFrame = new JFrame( "DNC Conversion" );
                ProgressPane progressPane = new ProgressPane( "Converting" );
                progressFrame.setContentPane( progressPane );
                progressFrame.setDefaultCloseOperation( DO_NOTHING_ON_CLOSE );
                progressFrame.pack( );
                progressFrame.setResizable( false );
                progressFrame.setLocationRelativeTo( frame );
                progressFrame.setVisible( true );

                // Worldwind's VPF reader doesn't handle interrupts well, so this is the best we can do
                AtomicBoolean cancelled = new AtomicBoolean( false );
                class CancellationException extends RuntimeException { }
                Runnable checkForCancellation = ( ) ->
                {
                    if ( cancelled.get( ) )
                    {
                        throw new CancellationException( );
                    }
                };

                startThread( "Converter", false, ( ) ->
                {
                    try
                    {
                        if ( dbNames.isEmpty( ) )
                        {
                            SwingUtilities.invokeLater( ( ) ->
                            {
                                progressFrame.dispose( );
                                showMessageDialog( frame, "No VPF databases to convert", "Error", ERROR_MESSAGE );
                                setTreeEnabled( contentPane, true );
                            } );
                        }
                        else
                        {
                            int stepsComplete = 0;
                            flatParentDir.mkdirs( );
                            for ( String dbName : dbNames )
                            {
                                File dbDir = dbDirs.get( dbName );
                                checkForCancellation.run( );
                                Database database = readVpfDatabase( dbDir );
                                checkForCancellation.run( );

                                stepsComplete++;
                                double fracCompleteA = stepsComplete / ( 2.0*dbNames.size( ) );
                                SwingUtilities.invokeAndWait( ( ) -> progressPane.setProgress( fracCompleteA ) );

                                String dirname = database.name.toLowerCase( ).replace( "dnc", "dncflat" );
                                checkForCancellation.run( );
                                File flatDir = createNewDir( flatParentDir, dirname );
                                checkForCancellation.run( );
                                writeFlatDatabase( database, flatDir, UTF_8 );
                                checkForCancellation.run( );

                                stepsComplete++;
                                double fracCompleteB = stepsComplete / ( 2.0*dbNames.size( ) );
                                SwingUtilities.invokeAndWait( ( ) -> progressPane.setProgress( fracCompleteB ) );
                            }

                            SwingUtilities.invokeLater( ( ) ->
                            {
                                progressFrame.dispose( );
                                showMessageDialog( frame, "Conversion succeeded", "Success", INFORMATION_MESSAGE );
                                setTreeEnabled( contentPane, true );
                            } );
                        }
                    }
                    catch ( CancellationException e )
                    {
                        SwingUtilities.invokeLater( ( ) ->
                        {
                            progressFrame.dispose( );
                            showMessageDialog( frame, "Conversion cancelled", "Cancelled", INFORMATION_MESSAGE );
                            setTreeEnabled( contentPane, true );
                        } );
                    }
                    catch ( Exception e )
                    {
                        SwingUtilities.invokeLater( ( ) ->
                        {
                            progressFrame.dispose( );

                            JLabel summaryLabel = new JLabel( "Conversion failed" );

                            JTextArea detailArea = new JTextArea( );
                            StringWriter detailText = new StringWriter( );
                            e.printStackTrace( new PrintWriter( detailText ) );
                            detailArea.setText( detailText.toString( ) );
                            detailArea.setCaretPosition( 0 );

                            JScrollPane detailScroller = new JScrollPane( detailArea, VERTICAL_SCROLLBAR_ALWAYS, HORIZONTAL_SCROLLBAR_AS_NEEDED );

                            JPanel messagePanel = new JPanel( new MigLayout( ) );
                            messagePanel.add( summaryLabel, "wrap" );
                            messagePanel.add( detailScroller, "height ::250" );

                            showMessageDialog( frame, messagePanel, "Error", ERROR_MESSAGE );
                            setTreeEnabled( contentPane, true );
                        } );
                    }
                } );

                progressPane.addCancelListener( ( ) ->
                {
                    cancelled.set( true );
                } );
            } );


            // Show frame
            //

            frame.setDefaultCloseOperation( DISPOSE_ON_CLOSE );
            frame.pack( );
            frame.setMinimumSize( frame.getPreferredSize( ) );
            frame.setLocationRelativeTo( null );
            frame.setVisible( true );

        } );
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy