/*
 *  soapUI, copyright (C) 2004-2012 smartbear.com 
 *
 *  soapUI is free software; you can redistribute it and/or modify it under the 
 *  terms of version 2.1 of the GNU Lesser General Public License as published by 
 *  the Free Software Foundation.
 *
 *  soapUI is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without 
 *  even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
 *  See the GNU Lesser General Public License for more details at gnu.org.
 */

package com.eviware.soapui.impl.wsdl.panels.teststeps;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DropTarget;
import java.awt.event.ActionEvent;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.DefaultCellEditor;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.JPasswordField;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTabbedPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.JToggleButton;
import javax.swing.ListModel;
import javax.swing.SwingUtilities;
import javax.swing.TransferHandler;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.text.Document;

import org.apache.log4j.Logger;
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;

import com.eviware.soapui.SoapUI;
import com.eviware.soapui.config.JdbcRequestTestStepConfig;
import com.eviware.soapui.impl.support.actions.ShowOnlineHelpAction;
import com.eviware.soapui.impl.support.components.ModelItemXmlEditor;
import com.eviware.soapui.impl.support.components.ResponseMessageXmlEditor;
import com.eviware.soapui.impl.support.panels.AbstractHttpRequestDesktopPanel;
import com.eviware.soapui.impl.wsdl.MutableTestPropertyHolder;
import com.eviware.soapui.impl.wsdl.panels.teststeps.support.DefaultPropertyTableHolderModel;
import com.eviware.soapui.impl.wsdl.panels.teststeps.support.PropertyHolderTable;
import com.eviware.soapui.impl.wsdl.support.HelpUrls;
import com.eviware.soapui.impl.wsdl.testcase.WsdlTestRunContext;
import com.eviware.soapui.impl.wsdl.teststeps.JdbcRequestTestStep;
import com.eviware.soapui.impl.wsdl.teststeps.WsdlTestStepWithProperties;
import com.eviware.soapui.impl.wsdl.teststeps.actions.AddAssertionAction;
import com.eviware.soapui.model.ModelItem;
import com.eviware.soapui.model.iface.Request.SubmitException;
import com.eviware.soapui.model.iface.Submit;
import com.eviware.soapui.model.iface.Submit.Status;
import com.eviware.soapui.model.iface.SubmitContext;
import com.eviware.soapui.model.iface.SubmitListener;
import com.eviware.soapui.model.propertyexpansion.DefaultPropertyExpansionContext;
import com.eviware.soapui.model.propertyexpansion.PropertyExpansionContext;
import com.eviware.soapui.model.testsuite.Assertable;
import com.eviware.soapui.model.testsuite.Assertable.AssertionStatus;
import com.eviware.soapui.model.testsuite.AssertionsListener;
import com.eviware.soapui.model.testsuite.LoadTestRunner;
import com.eviware.soapui.model.testsuite.TestAssertion;
import com.eviware.soapui.model.testsuite.TestCaseRunner;
import com.eviware.soapui.monitor.support.TestMonitorListenerAdapter;
import com.eviware.soapui.security.SecurityTestRunner;
import com.eviware.soapui.settings.UISettings;
import com.eviware.soapui.support.DateUtil;
import com.eviware.soapui.support.DocumentListenerAdapter;
import com.eviware.soapui.support.ListDataChangeListener;
import com.eviware.soapui.support.SoapUIException;
import com.eviware.soapui.support.StringUtils;
import com.eviware.soapui.support.UISupport;
import com.eviware.soapui.support.actions.ChangeSplitPaneOrientationAction;
import com.eviware.soapui.support.components.JComponentInspector;
import com.eviware.soapui.support.components.JEditorStatusBarWithProgress;
import com.eviware.soapui.support.components.JInspectorPanel;
import com.eviware.soapui.support.components.JInspectorPanelFactory;
import com.eviware.soapui.support.components.JUndoableTextField;
import com.eviware.soapui.support.components.JXToolBar;
import com.eviware.soapui.support.components.SimpleForm;
import com.eviware.soapui.support.editor.xml.support.AbstractXmlDocument;
import com.eviware.soapui.support.jdbc.JdbcUtils;
import com.eviware.soapui.support.log.JLogList;
import com.eviware.soapui.support.propertyexpansion.PropertyExpansionPopupListener;
import com.eviware.soapui.support.types.StringToStringMap;
import com.eviware.soapui.support.xml.SyntaxEditorUtil;
import com.eviware.soapui.ui.support.ModelItemDesktopPanel;

public class JdbcRequestTestStepDesktopPanel extends ModelItemDesktopPanel<JdbcRequestTestStep> implements
		SubmitListener
{
	private final static Logger log = Logger.getLogger( AbstractHttpRequestDesktopPanel.class );
	protected JPanel configPanel;
	private JButton addAssertionButton;
	protected JInspectorPanel inspectorPanel;
	protected JdbcRequestTestStep jdbcRequestTestStep;
	protected JComponentInspector<?> assertionInspector;
	protected AssertionsPanel assertionsPanel;
	private InternalAssertionsListener assertionsListener = new InternalAssertionsListener();
	private InternalTestMonitorListener testMonitorListener = new InternalTestMonitorListener();
	protected JComponent requestEditor;
	protected ModelItemXmlEditor<?, ?> responseEditor;
	protected JPanel panel;
	protected SimpleForm configForm;
	protected static final String DRIVER_FIELD = "Driver";
	protected static final String CONNSTR_FIELD = "Connection String";
	protected static final String PASS_FIELD = "Password";
	public static final String QUERY_FIELD = "SQL Query";
	protected static final String STOREDPROCEDURE_FIELD = "Stored Procedure";
	protected static final String DATA_CONNECTION_FIELD = "Connection";

	protected static final String QUERY_ELEMENT = "query";
	protected static final String STOREDPROCEDURE_ELEMENT = "stored-procedure";
	protected Connection connection;
	protected RSyntaxTextArea queryArea;
	protected JCheckBox isStoredProcedureCheckBox;
	protected JTextField driverTextField;
	protected JTextField connStrTextField;
	protected JButton testConnectionButton;
	protected JPasswordField passField;
	private Submit submit;
	private SubmitAction submitAction;
	protected JButton submitButton;
	private JToggleButton tabsButton;
	private JTabbedPane requestTabs;
	private JPanel requestTabPanel;
	private boolean responseHasFocus;
	private JSplitPane requestSplitPane;
	private JEditorStatusBarWithProgress statusBar;
	private JButton cancelButton;
	private JButton splitButton;
	protected JComponent propertiesTableComponent;
	private JComponentInspector<?> logInspector;
	protected JLogList logArea;
	private long startTime;
	protected JButton reconfigureConnPropertiesButton;
	protected PropertyHolderTable propertyHolderTable;
	protected JdbcRequestTestStepConfig jdbcRequestTestStepConfig;

	public JdbcRequestTestStepDesktopPanel( JdbcRequestTestStep modelItem )
	{
		super( modelItem );
		jdbcRequestTestStep = modelItem;
		initConfig();
		initContent();

		SoapUI.getTestMonitor().addTestMonitorListener( testMonitorListener );
		setEnabled( !SoapUI.getTestMonitor().hasRunningTest( jdbcRequestTestStep.getTestCase() ) );

		jdbcRequestTestStep.addAssertionsListener( assertionsListener );
	}

	// added this again cause without it connection is set to none whenever jdbc
	// test step is reopened
	protected void initConfig()
	{
		jdbcRequestTestStepConfig = jdbcRequestTestStep.getJdbcRequestTestStepConfig();
	}

	protected JComponent buildContent()
	{
		requestSplitPane = UISupport.createHorizontalSplit();
		requestSplitPane.setResizeWeight( 0.5 );
		requestSplitPane.setBorder( null );

		JComponent content;
		submitAction = new SubmitAction();
		submitButton = createActionButton( submitAction, true );
		submitButton.setEnabled( enableSubmit() );

		cancelButton = createActionButton( new CancelAction(), false );
		tabsButton = new JToggleButton( new ChangeToTabsAction() );
		tabsButton.setPreferredSize( UISupport.TOOLBAR_BUTTON_DIMENSION );
		splitButton = createActionButton( new ChangeSplitPaneOrientationAction( requestSplitPane ), true );

		addAssertionButton = UISupport.createToolbarButton( new AddAssertionAction( jdbcRequestTestStep ) );
		addAssertionButton.setEnabled( true );

		requestTabs = new JTabbedPane();
		requestTabs.addChangeListener( new ChangeListener()
		{

			public void stateChanged( ChangeEvent e )
			{
				SwingUtilities.invokeLater( new Runnable()
				{

					public void run()
					{
						int ix = requestTabs.getSelectedIndex();
						if( ix == 0 )
							requestEditor.requestFocus();
						else if( ix == 1 && responseEditor != null )
							responseEditor.requestFocus();
					}
				} );
			}
		} );

		addFocusListener( new FocusAdapter()
		{

			@Override
			public void focusGained( FocusEvent e )
			{
				if( requestTabs.getSelectedIndex() == 1 || responseHasFocus )
					responseEditor.requestFocusInWindow();
				else
					requestEditor.requestFocusInWindow();
			}
		} );

		requestTabPanel = UISupport.createTabPanel( requestTabs, true );

		requestEditor = buildRequestConfigPanel();
		responseEditor = buildResponseEditor();
		if( jdbcRequestTestStep.getSettings().getBoolean( UISettings.START_WITH_REQUEST_TABS ) )
		{
			requestTabs.addTab( "Request", requestEditor );
			if( responseEditor != null )
				requestTabs.addTab( "Response", responseEditor );
			tabsButton.setSelected( true );
			splitButton.setEnabled( false );

			content = requestTabPanel;
		}
		else
		{
			requestSplitPane.setTopComponent( requestEditor );
			requestSplitPane.setBottomComponent( responseEditor );
			requestSplitPane.setDividerLocation( 0.5 );
			content = requestSplitPane;
		}

		inspectorPanel = JInspectorPanelFactory.build( content );
		inspectorPanel.setDefaultDividerLocation( 0.7F );
		add( buildToolbar(), BorderLayout.NORTH );
		add( inspectorPanel.getComponent(), BorderLayout.CENTER );
		assertionsPanel = buildAssertionsPanel();

		assertionInspector = new JComponentInspector<JComponent>( assertionsPanel, "Assertions ("
				+ getModelItem().getAssertionCount() + ")", "Assertions for this Test Request", true );

		inspectorPanel.addInspector( assertionInspector );
		logInspector = new JComponentInspector<JComponent>( buildLogPanel(), "Request Log (0)", "Log of requests", true );
		inspectorPanel.addInspector( logInspector );
		inspectorPanel.setCurrentInspector( "Assertions" );

		updateStatusIcon();

		return inspectorPanel.getComponent();
	}

	protected JComponent buildRequestConfigPanel()
	{
		configPanel = UISupport.addTitledBorder( new JPanel( new BorderLayout() ), "Configuration" );
		if( panel == null )
		{
			panel = new JPanel( new BorderLayout() );
			configForm = new SimpleForm();
			createSimpleJdbcConfigForm();
			addStoreProcedureChangeListener();

			panel.add( new JScrollPane( configForm.getPanel() ) );
		}
		configPanel.add( panel, BorderLayout.CENTER );

		propertiesTableComponent = buildProperties();
		JSplitPane split = UISupport.createVerticalSplit( propertiesTableComponent, configPanel );
		split.setDividerLocation( 120 );
		split.setPreferredSize( new Dimension( 330, 500 ) );

		return new JScrollPane( split );

	}

	protected void initContent()
	{
		jdbcRequestTestStep.getJdbcRequest().addSubmitListener( this );

		add( buildContent(), BorderLayout.CENTER );
		add( buildToolbar(), BorderLayout.NORTH );
		add( buildStatusLabel(), BorderLayout.SOUTH );

		setPreferredSize( new Dimension( 600, 500 ) );

		addFocusListener( new FocusAdapter()
		{

			@Override
			public void focusGained( FocusEvent e )
			{
				if( requestTabs.getSelectedIndex() == 1 || responseHasFocus )
					responseEditor.requestFocusInWindow();
				else
					requestEditor.requestFocusInWindow();
			}
		} );
	}

	protected JComponent buildStatusLabel()
	{
		statusBar = new JEditorStatusBarWithProgress();
		statusBar.setBorder( BorderFactory.createEmptyBorder( 1, 0, 0, 0 ) );

		return statusBar;
	}

	protected JComponent buildProperties()
	{
		propertyHolderTable = new PropertyHolderTable( getModelItem() )
		{
			protected JTable buildPropertiesTable()
			{
				propertiesModel = new DefaultPropertyTableHolderModel( holder )
				{
					@Override
					public String[] getPropertyNames()
					{
						List<String> propertyNamesList = new ArrayList<String>();
						for( String name : holder.getPropertyNames() )
						{
							if( name.equals( WsdlTestStepWithProperties.RESPONSE_AS_XML ) )
							{
								continue;
							}
							propertyNamesList.add( name );
						}
						return propertyNamesList.toArray( new String[propertyNamesList.size()] );
					}
				};
				propertiesTable = new PropertiesHolderJTable();
				propertiesTable.setSurrendersFocusOnKeystroke( true );

				propertiesTable.putClientProperty( "terminateEditOnFocusLost", Boolean.TRUE );
				propertiesTable.getSelectionModel().addListSelectionListener( new ListSelectionListener()
				{
					public void valueChanged( ListSelectionEvent e )
					{
						int selectedRow = propertiesTable.getSelectedRow();
						if( removePropertyAction != null )
							removePropertyAction.setEnabled( selectedRow != -1 );

						if( movePropertyUpAction != null )
							movePropertyUpAction.setEnabled( selectedRow > 0 );

						if( movePropertyDownAction != null )
							movePropertyDownAction.setEnabled( selectedRow >= 0
									&& selectedRow < propertiesTable.getRowCount() - 1 );
					}
				} );

				propertiesTable.setDragEnabled( true );
				propertiesTable.setTransferHandler( new TransferHandler( "testProperty" ) );

				if( getHolder().getModelItem() != null )
				{
					DropTarget dropTarget = new DropTarget( propertiesTable,
							new PropertyHolderTablePropertyExpansionDropTarget() );
					dropTarget.setDefaultActions( DnDConstants.ACTION_COPY_OR_MOVE );
				}

				return propertiesTable;
			}
		};

		JUndoableTextField textField = new JUndoableTextField( true );

		PropertyExpansionPopupListener.enable( textField, getModelItem() );
		propertyHolderTable.getPropertiesTable().setDefaultEditor( String.class, new DefaultCellEditor( textField ) );

		return propertyHolderTable;
	}

	public PropertyHolderTable getPropertyHolderTable()
	{
		return propertyHolderTable;
	}

	public void setPropertyHolderTable( StringToStringMap preparedProperties )
	{
		// first remove the old content
		String[] names = propertyHolderTable.getHolder().getPropertyNames();
		if( names.length > 0 )
		{
			for( String propertyName : names )
			{
				( ( MutableTestPropertyHolder )propertyHolderTable.getHolder() ).removeProperty( propertyName );
			}
		}
		propertyHolderTable.getPropertiesTable().removeAll();
		if( preparedProperties != null )
		{
			int i = 0;
			for( String key : preparedProperties.keySet() )
			{
				String value = preparedProperties.get( key );
				( ( MutableTestPropertyHolder )propertyHolderTable.getHolder() ).addProperty( key );
				( ( MutableTestPropertyHolder )propertyHolderTable.getHolder() ).setPropertyValue( key, value );
				i++ ;
			}
		}
	}

	protected JComponent buildToolbar()
	{
		JXToolBar toolbar = UISupport.createToolbar();

		toolbar.setBorder( BorderFactory.createEmptyBorder( 2, 2, 2, 2 ) );

		toolbar.addFixed( submitButton );
		toolbar.add( cancelButton );
		toolbar.addFixed( addAssertionButton );

		toolbar.add( Box.createHorizontalGlue() );
		toolbar.add( tabsButton );
		toolbar.add( splitButton );
		toolbar.addFixed( UISupport.createToolbarButton( new ShowOnlineHelpAction( HelpUrls.JDBCSTEPEDITOR_HELP_URL ) ) );
		return toolbar;

	}

	public JdbcRequestTestStep getJdbcRequestTestStep()
	{
		return jdbcRequestTestStep;
	}

	public void setQuery( String query )
	{
		if( configForm != null )
		{
			configForm.setComponentValue( QUERY_FIELD, query );
			jdbcRequestTestStep.setQuery( query );
		}
		else
		{
			// this.query = query;
			jdbcRequestTestStep.setQuery( query );
		}
	}

	protected JComponent buildLogPanel()
	{
		logArea = new JLogList( "Request Log" );

		logArea.getLogList().getModel().addListDataListener( new ListDataChangeListener()
		{
			public void dataChanged( ListModel model )
			{
				logInspector.setTitle( "Request Log (" + model.getSize() + ")" );
			}
		} );

		return logArea;
	}

	protected AssertionsPanel buildAssertionsPanel()
	{
		return new JdbcAssertionsPanel( jdbcRequestTestStep )
		{
		};
	}

	protected class JdbcAssertionsPanel extends AssertionsPanel
	{
		public JdbcAssertionsPanel( Assertable assertable )
		{
			super( assertable );
		}
	}

	protected void createSimpleJdbcConfigForm()
	{
		configForm.addSpace( 5 );
		configForm.setDefaultTextFieldColumns( 50 );

		driverTextField = configForm.appendTextField( DRIVER_FIELD, "JDBC Driver to use" );
		driverTextField.setText( jdbcRequestTestStep.getDriver() );
		PropertyExpansionPopupListener.enable( driverTextField, jdbcRequestTestStep );
		addDriverDocumentListener();

		connStrTextField = configForm.appendTextField( CONNSTR_FIELD, "JDBC Driver Connection String" );
		connStrTextField.setText( jdbcRequestTestStep.getConnectionString() );
		PropertyExpansionPopupListener.enable( connStrTextField, jdbcRequestTestStep );
		addConnStrDocumentListener();

		passField = configForm.appendPasswordField( PASS_FIELD, "Connection string Password" );
		passField.setVisible( false );
		passField.setText( jdbcRequestTestStep.getPassword() );
		addPasswordDocumentListener();

		reconfigureConnPropertiesButton = new JButton();
		configForm.addLeftComponent( reconfigureConnPropertiesButton );
		reconfigureConnPropertiesButton.setVisible( false );
		configForm.appendSeparator();

		testConnectionButton = configForm.appendButton( "TestConnection", "Test selected database connection" );
		testConnectionButton.setAction( new TestConnectionAction() );
		testConnectionButton.setEnabled( enableTestConnection() );
		submitButton.setEnabled( enableSubmit() );

		queryArea = SyntaxEditorUtil.createDefaultSQLSyntaxTextArea();
		PropertyExpansionPopupListener.enable( queryArea, jdbcRequestTestStep );
		queryArea.setText( jdbcRequestTestStep.getQuery() );
		JScrollPane scrollPane = new JScrollPane( queryArea );
		scrollPane.setPreferredSize( new Dimension( 400, 150 ) );
		configForm.append( QUERY_FIELD, scrollPane );
		queryArea.getDocument().addDocumentListener( new DocumentListenerAdapter()
		{

			@Override
			public void update( Document document )
			{
				jdbcRequestTestStep.setQuery( queryArea.getText() );
				submitButton.setEnabled( enableSubmit() );
			}
		} );

		isStoredProcedureCheckBox = configForm.appendCheckBox( STOREDPROCEDURE_FIELD,
				"Select if this is a stored procedure", jdbcRequestTestStep.isStoredProcedure() );
	}

	protected void addPasswordDocumentListener()
	{
		passField.getDocument().addDocumentListener( new DocumentListenerAdapter()
		{

			@Override
			public void update( Document document )
			{
				jdbcRequestTestStep.setPassword( configForm.getComponentValue( PASS_FIELD ) );
				testConnectionButton.setEnabled( enableTestConnection() );
				submitButton.setEnabled( enableSubmit() );
			}
		} );
	}

	protected void addConnStrDocumentListener()
	{
		connStrTextField.getDocument().addDocumentListener( new DocumentListenerAdapter()
		{
			@Override
			public void update( Document document )
			{
				jdbcRequestTestStep.setConnectionString( configForm.getComponentValue( CONNSTR_FIELD ) );
				testConnectionButton.setEnabled( enableTestConnection() );
				submitButton.setEnabled( enableSubmit() );
			}
		} );
	}

	protected void addDriverDocumentListener()
	{
		driverTextField.getDocument().addDocumentListener( new DocumentListenerAdapter()
		{
			@Override
			public void update( Document document )
			{
				jdbcRequestTestStep.setDriver( configForm.getComponentValue( DRIVER_FIELD ) );
				testConnectionButton.setEnabled( enableTestConnection() );
				submitButton.setEnabled( enableSubmit() );
			}
		} );
	}

	protected void addStoreProcedureChangeListener()
	{
		isStoredProcedureCheckBox.addItemListener( new ItemListener()
		{
			public void itemStateChanged( ItemEvent arg0 )
			{
				jdbcRequestTestStep.setStoredProcedure( ( ( JCheckBox )arg0.getSource() ).isSelected() );
				SwingUtilities.invokeLater( new Runnable()
				{

					public void run()
					{
						if( UISupport.confirm( "Would you like to empty current sql and properties?",
								"Reset query and properties" ) )
						{
							queryArea.setText( "" );
							( ( JdbcRequestTestStep )getPropertyHolderTable().getHolder() ).removeAllProperties();
						}
					}
				} );
			}
		} );
	}

	protected boolean enableTestConnection()
	{
		if( StringUtils.isNullOrEmpty( jdbcRequestTestStep.getDriver() )
				|| StringUtils.isNullOrEmpty( jdbcRequestTestStep.getConnectionString() )
				|| ( JdbcRequestTestStep.isNeededPassword( jdbcRequestTestStep.getConnectionString() ) && StringUtils
						.isNullOrEmpty( jdbcRequestTestStep.getPassword() ) ) )
		{
			return false;
		}
		else
		{
			if( jdbcRequestTestStep.getConnectionString().contains( JdbcRequestTestStep.PASS_TEMPLATE ) )
			{
				return !StringUtils.isNullOrEmpty( jdbcRequestTestStep.getPassword() );
			}
			else
			{
				return true;
			}
		}
	}

	protected boolean enableSubmit()
	{
		return enableTestConnection() && !StringUtils.isNullOrEmpty( jdbcRequestTestStep.getQuery() );
	}

	protected ModelItemXmlEditor<?, ?> buildResponseEditor()
	{
		return new JdbcResponseMessageEditor();
	}

	public class JdbcResponseMessageEditor extends ResponseMessageXmlEditor<JdbcRequestTestStep, JdbcResponseDocument>
	{
		public JdbcResponseMessageEditor()
		{
			super( new JdbcResponseDocument(), jdbcRequestTestStep );
		}
	}

	public boolean dependsOn( ModelItem modelItem )
	{
		return modelItem == getModelItem() || modelItem == getModelItem().getTestCase()
				|| modelItem == getModelItem().getTestCase().getTestSuite()
				|| modelItem == getModelItem().getTestCase().getTestSuite().getProject();
	}

	public boolean onClose( boolean canCancel )
	{
		configPanel.removeAll();

		SoapUI.getTestMonitor().removeTestMonitorListener( testMonitorListener );
		jdbcRequestTestStep.removeAssertionsListener( assertionsListener );
		jdbcRequestTestStep.getJdbcRequest().removeSubmitListener( this );

		responseEditor.release();
		assertionsPanel.release();
		inspectorPanel.release();
		propertyHolderTable.release();

		return release();
	}

	public class JdbcResponseDocument extends AbstractXmlDocument implements PropertyChangeListener
	{
		public JdbcResponseDocument()
		{
			jdbcRequestTestStep.addPropertyChangeListener( JdbcRequestTestStep.RESPONSE_PROPERTY, this );
		}

		public void propertyChange( PropertyChangeEvent evt )
		{
			fireXmlChanged( evt.getOldValue() == null ? null : ( ( JdbcResponse )evt.getOldValue() ).getContentAsString(),
					getXml() );
		}

		public String getXml()
		{
			JdbcResponse response = jdbcRequestTestStep.getJdbcRequest().getResponse();
			return response == null ? null : response.getContentAsString();
		}

		public void setXml( String xml )
		{
			if( jdbcRequestTestStep.getJdbcRequest().getResponse() != null )
				jdbcRequestTestStep.getJdbcRequest().getResponse().setContentAsString( xml );
		}

		public void release()
		{
			super.release();
			jdbcRequestTestStep.removePropertyChangeListener( JdbcRequestTestStep.RESPONSE_PROPERTY, this );
		}
	}

	protected class TestConnectionAction extends AbstractAction
	{
		public TestConnectionAction()
		{
			putValue( Action.SMALL_ICON, UISupport.createImageIcon( "/run_testcase.gif" ) );
			putValue( Action.SHORT_DESCRIPTION, "Test the current Connection" );

			setEnabled( false );
		}

		public void actionPerformed( ActionEvent arg0 )
		{
			try
			{

				PropertyExpansionContext context = new DefaultPropertyExpansionContext( getModelItem() );
				JdbcUtils.initConnection( context, jdbcRequestTestStep.getDriver(),
						jdbcRequestTestStep.getConnectionString(), jdbcRequestTestStep.getPassword() );
				UISupport.showInfoMessage( "The Connection Successfully Tested" );
			}
			catch( SoapUIException e )
			{
				SoapUI.logError( e );
				UISupport.showErrorMessage( e.toString() );
			}
			catch( SQLException e )
			{
				SoapUI.logError( e );
				UISupport.showErrorMessage( "Can't get the Connection for specified properties; " + e.toString() );
			}
		}
	}

	private class InternalTestMonitorListener extends TestMonitorListenerAdapter
	{
		public void loadTestFinished( LoadTestRunner runner )
		{
			setEnabled( !SoapUI.getTestMonitor().hasRunningTest( getModelItem().getTestCase() ) );
		}

		public void loadTestStarted( LoadTestRunner runner )
		{
			if( runner.getLoadTest().getTestCase() == getModelItem().getTestCase() )
				setEnabled( false );
		}

		public void securityTestFinished( SecurityTestRunner runner )
		{
			setEnabled( !SoapUI.getTestMonitor().hasRunningTest( getModelItem().getTestCase() ) );
		}

		public void securityTestStarted( SecurityTestRunner runner )
		{
			if( runner.getSecurityTest().getTestCase() == getModelItem().getTestCase() )
				setEnabled( false );
		}

		public void testCaseFinished( TestCaseRunner runner )
		{
			setEnabled( !SoapUI.getTestMonitor().hasRunningTest( getModelItem().getTestCase() ) );
		}

		public void testCaseStarted( TestCaseRunner runner )
		{
			if( runner.getTestCase() == getModelItem().getTestCase() )
				setEnabled( false );
		}
	}

	public class SubmitAction extends AbstractAction
	{
		public SubmitAction()
		{
			putValue( Action.SMALL_ICON, UISupport.createImageIcon( "/submit_request.gif" ) );
			putValue( Action.SHORT_DESCRIPTION, "Submit request to specified database" );
			putValue( Action.ACCELERATOR_KEY, UISupport.getKeyStroke( "alt ENTER" ) );
		}

		public void actionPerformed( ActionEvent e )
		{
			onSubmit();
		}
	}

	protected void onSubmit()
	{
		if( submit != null && submit.getStatus() == Submit.Status.RUNNING )
		{
			if( UISupport.confirm( "Cancel current request?", "Submit Request" ) )
			{
				submit.cancel();
			}
			else
				return;
		}

		try
		{
			submit = doSubmit();
		}
		catch( SubmitException e1 )
		{
			SoapUI.logError( e1 );
		}
	}

	protected Submit doSubmit() throws SubmitException
	{
		return jdbcRequestTestStep.getJdbcRequest().submit( new WsdlTestRunContext( getModelItem() ), true );
	}

	private final class ChangeToTabsAction extends AbstractAction
	{
		public ChangeToTabsAction()
		{
			putValue( Action.SMALL_ICON, UISupport.createImageIcon( "/toggle_tabs.gif" ) );
			putValue( Action.SHORT_DESCRIPTION, "Toggles to tab-based layout" );
		}

		public void actionPerformed( ActionEvent e )
		{
			if( splitButton.isEnabled() )
			{
				splitButton.setEnabled( false );
				removeContent( requestSplitPane );
				setContent( requestTabPanel );
				requestTabs.addTab( "Request", requestEditor );

				if( responseEditor != null )
					requestTabs.addTab( "Response", responseEditor );

				if( responseHasFocus )
				{
					requestTabs.setSelectedIndex( 1 );
					requestEditor.requestFocus();
				}
				requestTabs.repaint();
			}
			else
			{
				int selectedIndex = requestTabs.getSelectedIndex();

				splitButton.setEnabled( true );
				removeContent( requestTabPanel );
				setContent( requestSplitPane );
				requestSplitPane.setTopComponent( requestEditor );
				if( responseEditor != null )
					requestSplitPane.setBottomComponent( responseEditor );
				requestSplitPane.setDividerLocation( 0.5 );

				if( selectedIndex == 0 || responseEditor == null )
					requestEditor.requestFocus();
				else
					responseEditor.requestFocus();
				requestSplitPane.repaint();
			}

			revalidate();
		}
	}

	public void setContent( JComponent content )
	{
		inspectorPanel.setContentComponent( content );
	}

	public void removeContent( JComponent content )
	{
		inspectorPanel.setContentComponent( null );
	}

	private class CancelAction extends AbstractAction
	{
		public CancelAction()
		{
			super();
			putValue( Action.SMALL_ICON, UISupport.createImageIcon( "/cancel_request.gif" ) );
			putValue( Action.SHORT_DESCRIPTION, "Aborts ongoing request" );
			putValue( Action.ACCELERATOR_KEY, UISupport.getKeyStroke( "alt X" ) );
		}

		public void actionPerformed( ActionEvent e )
		{
			onCancel();
		}
	}

	protected void onCancel()
	{
		if( submit == null )
			return;

		cancelButton.setEnabled( false );
		submit.cancel();
		setEnabled( true );
		submit = null;
	}

	public void setEnabled( boolean enabled )
	{
		if( responseEditor != null )
			responseEditor.setEditable( enabled );

		submitButton.setEnabled( enabled );
		addAssertionButton.setEnabled( enabled );
		driverTextField.setEnabled( enabled );
		connStrTextField.setEnabled( enabled );
		passField.setEnabled( enabled );
		queryArea.setEnabled( enabled );
		queryArea.setEditable( enabled );
		isStoredProcedureCheckBox.setEnabled( enabled );
		propertiesTableComponent.setEnabled( enabled );
		testConnectionButton.setEnabled( enabled );

		statusBar.setIndeterminate( !enabled );
	}

	public void afterSubmit( Submit submit, SubmitContext context )
	{
		if( submit.getRequest() != jdbcRequestTestStep.getJdbcRequest() )
			return;

		Status status = submit.getStatus();
		JdbcResponse response = ( JdbcResponse )submit.getResponse();
		if( status == Status.FINISHED )
		{
			jdbcRequestTestStep.setResponse( response, context );
		}

		cancelButton.setEnabled( false );
		setEnabled( true );

		String message = null;
		String infoMessage = null;
		String requestName = jdbcRequestTestStep.getName();
		String rawSql = "";

		if( status == Status.CANCELED )
		{
			message = "CANCELED";
			infoMessage = "[" + requestName + "] - CANCELED";
		}
		else
		{
			if( status == Status.ERROR || response == null )
			{
				message = "Error getting response; " + submit.getError();
				infoMessage = "Error getting response for [" + requestName + "]; " + submit.getError();
			}
			else
			{
				message = "response time: " + response.getTimeTaken() + "ms (" + response.getContentLength() + " bytes)";
				infoMessage = "Got response for [" + requestName + "] in " + response.getTimeTaken() + "ms ("
						+ response.getContentLength() + " bytes)";
				rawSql = ( ( JdbcSubmit )submit ).getRawSql();

				if( !splitButton.isEnabled() )
					requestTabs.setSelectedIndex( 1 );

				responseEditor.requestFocus();
			}
		}
		if( !StringUtils.isNullOrEmpty( rawSql ) )
			logMessages( "Sql executed: " + rawSql, rawSql );
		logMessages( message, infoMessage );

		if( getModelItem().getSettings().getBoolean( UISettings.AUTO_VALIDATE_RESPONSE ) )
			responseEditor.getSourceEditor().validate();

		JdbcRequestTestStepDesktopPanel.this.submit = null;

		updateStatusIcon();
	}

	protected void logMessages( String message, String infoMessage )
	{
		log.info( infoMessage );
		statusBar.setInfo( message );
		logArea.addLine( DateUtil.formatFull( new Date( startTime ) ) + " - " + message );
	}

	public boolean beforeSubmit( Submit submit, SubmitContext context )
	{
		if( submit.getRequest() != jdbcRequestTestStep.getJdbcRequest() )
			return true;

		setEnabled( false );
		cancelButton.setEnabled( JdbcRequestTestStepDesktopPanel.this.submit != null );
		startTime = System.currentTimeMillis();
		return true;
	}

	public void propertyChange( PropertyChangeEvent evt )
	{
		super.propertyChange( evt );

		if( evt.getPropertyName().equals( JdbcRequestTestStep.STATUS_PROPERTY ) )
			updateStatusIcon();
	}

	private final class InternalAssertionsListener implements AssertionsListener
	{
		public void assertionAdded( TestAssertion assertion )
		{
			assertionInspector.setTitle( "Assertions (" + getModelItem().getAssertionCount() + ")" );
		}

		public void assertionRemoved( TestAssertion assertion )
		{
			assertionInspector.setTitle( "Assertions (" + getModelItem().getAssertionCount() + ")" );
		}

		public void assertionMoved( TestAssertion assertion, int ix, int offset )
		{
			assertionInspector.setTitle( "Assertions (" + getModelItem().getAssertionCount() + ")" );
		}
	}

	private void updateStatusIcon()
	{
		AssertionStatus status = jdbcRequestTestStep.getAssertionStatus();
		switch( status )
		{
		case FAILED :
		{
			assertionInspector.setIcon( UISupport.createImageIcon( "/failed_assertion.gif" ) );
			inspectorPanel.activate( assertionInspector );
			break;
		}
		case UNKNOWN :
		{
			assertionInspector.setIcon( UISupport.createImageIcon( "/unknown_assertion.gif" ) );
			break;
		}
		case VALID :
		{
			assertionInspector.setIcon( UISupport.createImageIcon( "/valid_assertion.gif" ) );
			inspectorPanel.deactivate();
			break;
		}
		}
	}
}
