/*
 *  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;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.GeneralSecurityException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.swing.ImageIcon;
import javax.xml.namespace.QName;

import org.apache.commons.ssl.OpenSSL;
import org.apache.log4j.Logger;
import org.apache.xmlbeans.XmlError;
import org.apache.xmlbeans.XmlException;
import org.apache.xmlbeans.XmlObject;
import org.apache.xmlbeans.XmlOptions;

import com.eviware.soapui.SoapUI;
import com.eviware.soapui.config.CrossSiteScriptingScanConfig;
import com.eviware.soapui.config.GroovySecurityCheckConfig;
import com.eviware.soapui.config.GroovySecurityScanConfig;
import com.eviware.soapui.config.InterfaceConfig;
import com.eviware.soapui.config.InvalidSecurityCheckConfig;
import com.eviware.soapui.config.InvalidSecurityScanConfig;
import com.eviware.soapui.config.MaliciousAttachmentConfig;
import com.eviware.soapui.config.MaliciousAttachmentElementConfig;
import com.eviware.soapui.config.MaliciousAttachmentSecurityCheckConfig;
import com.eviware.soapui.config.MaliciousAttachmentSecurityScanConfig;
import com.eviware.soapui.config.MockServiceConfig;
import com.eviware.soapui.config.MockServiceDocumentConfig;
import com.eviware.soapui.config.ParameterExposureCheckConfig;
import com.eviware.soapui.config.ProjectConfig;
import com.eviware.soapui.config.SQLInjectionCheckConfig;
import com.eviware.soapui.config.SQLInjectionScanConfig;
import com.eviware.soapui.config.SchemaTypeForSecurityCheckConfig;
import com.eviware.soapui.config.SchemaTypeForSecurityScanConfig;
import com.eviware.soapui.config.SecurityCheckConfig;
import com.eviware.soapui.config.SecurityScanConfig;
import com.eviware.soapui.config.SecurityTestConfig;
import com.eviware.soapui.config.SoapuiProjectDocumentConfig;
import com.eviware.soapui.config.TestAssertionConfig;
import com.eviware.soapui.config.TestCaseConfig;
import com.eviware.soapui.config.TestStepSecurityTestConfig;
import com.eviware.soapui.config.TestSuiteConfig;
import com.eviware.soapui.config.TestSuiteDocumentConfig;
import com.eviware.soapui.config.TestSuiteRunTypesConfig;
import com.eviware.soapui.config.TestSuiteRunTypesConfig.Enum;
import com.eviware.soapui.config.XmlBombSecurityCheckConfig;
import com.eviware.soapui.config.XmlBombSecurityScanConfig;
import com.eviware.soapui.impl.WorkspaceImpl;
import com.eviware.soapui.impl.WsdlInterfaceFactory;
import com.eviware.soapui.impl.rest.support.RestRequestConverter.RestConversionException;
import com.eviware.soapui.impl.settings.XmlBeansSettingsImpl;
import com.eviware.soapui.impl.support.AbstractInterface;
import com.eviware.soapui.impl.support.EndpointSupport;
import com.eviware.soapui.impl.wsdl.endpoint.DefaultEndpointStrategy;
import com.eviware.soapui.impl.wsdl.mock.WsdlMockService;
import com.eviware.soapui.impl.wsdl.support.ExternalDependency;
import com.eviware.soapui.impl.wsdl.support.PathUtils;
import com.eviware.soapui.impl.wsdl.support.wsdl.UrlWsdlLoader;
import com.eviware.soapui.impl.wsdl.support.wsdl.WsdlLoader;
import com.eviware.soapui.impl.wsdl.support.wss.DefaultWssContainer;
import com.eviware.soapui.impl.wsdl.testcase.WsdlProjectRunner;
import com.eviware.soapui.model.ModelItem;
import com.eviware.soapui.model.environment.DefaultEnvironment;
import com.eviware.soapui.model.environment.Environment;
import com.eviware.soapui.model.environment.EnvironmentListener;
import com.eviware.soapui.model.environment.Property;
import com.eviware.soapui.model.iface.Interface;
import com.eviware.soapui.model.mock.MockService;
import com.eviware.soapui.model.project.EndpointStrategy;
import com.eviware.soapui.model.project.Project;
import com.eviware.soapui.model.project.ProjectListener;
import com.eviware.soapui.model.propertyexpansion.DefaultPropertyExpansionContext;
import com.eviware.soapui.model.propertyexpansion.PropertyExpansion;
import com.eviware.soapui.model.propertyexpansion.PropertyExpansionContainer;
import com.eviware.soapui.model.propertyexpansion.PropertyExpansionContext;
import com.eviware.soapui.model.settings.Settings;
import com.eviware.soapui.model.support.ModelSupport;
import com.eviware.soapui.model.testsuite.ProjectRunContext;
import com.eviware.soapui.model.testsuite.ProjectRunListener;
import com.eviware.soapui.model.testsuite.ProjectRunner;
import com.eviware.soapui.model.testsuite.TestRunnable;
import com.eviware.soapui.model.testsuite.TestSuite;
import com.eviware.soapui.model.testsuite.TestSuite.TestSuiteRunType;
import com.eviware.soapui.settings.ProjectSettings;
import com.eviware.soapui.settings.UISettings;
import com.eviware.soapui.settings.WsdlSettings;
import com.eviware.soapui.support.SoapUIException;
import com.eviware.soapui.support.StringUtils;
import com.eviware.soapui.support.Tools;
import com.eviware.soapui.support.UISupport;
import com.eviware.soapui.support.resolver.ResolveContext;
import com.eviware.soapui.support.resolver.ResolveDialog;
import com.eviware.soapui.support.scripting.SoapUIScriptEngine;
import com.eviware.soapui.support.scripting.SoapUIScriptEngineRegistry;
import com.eviware.soapui.support.types.StringToObjectMap;
import com.eviware.soapui.support.xml.XmlUtils;

/**
 * WSDL project implementation
 * 
 * @author Ole.Matzura
 */

public class WsdlProject extends AbstractTestPropertyHolderWsdlModelItem<ProjectConfig> implements Project,
		PropertyExpansionContainer, PropertyChangeListener, TestRunnable
{
	public final static String AFTER_LOAD_SCRIPT_PROPERTY = WsdlProject.class.getName() + "@setupScript";
	public final static String BEFORE_SAVE_SCRIPT_PROPERTY = WsdlProject.class.getName() + "@tearDownScript";
	public final static String RESOURCE_ROOT_PROPERTY = WsdlProject.class.getName() + "@resourceRoot";

	private WorkspaceImpl workspace;
	protected String path;
	protected List<AbstractInterface<?>> interfaces = new ArrayList<AbstractInterface<?>>();
	protected List<WsdlTestSuite> testSuites = new ArrayList<WsdlTestSuite>();
	protected List<WsdlMockService> mockServices = new ArrayList<WsdlMockService>();
	protected Set<ProjectListener> projectListeners = new HashSet<ProjectListener>();
	protected SoapuiProjectDocumentConfig projectDocument;
	private ImageIcon disabledIcon;
	private ImageIcon closedIcon;
	private ImageIcon remoteIcon;
	private ImageIcon openEncyptedIcon;
	protected EndpointStrategy endpointStrategy = new DefaultEndpointStrategy();
	protected long lastModified;
	private boolean remote;
	private boolean open = true;
	private boolean disabled;

	private SoapUIScriptEngine afterLoadScriptEngine;
	private SoapUIScriptEngine beforeSaveScriptEngine;
	private PropertyExpansionContext context = new DefaultPropertyExpansionContext( this );
	protected DefaultWssContainer wssContainer;
	private String projectPassword = null;
	private String hermesConfig;
	private boolean wrongPasswordSupplied;

	protected Set<EnvironmentListener> environmentListeners = new HashSet<EnvironmentListener>();

	/*
	 * 3 state flag: 1. 0 - project not encrypted 2. 1 - encrypted , good
	 * password, means that it could be successfully decrypted 3. -1 - encrypted,
	 * but with bad password or no password.
	 */

	protected int encrypted;
	private ImageIcon closedEncyptedIcon;
	private SoapUIScriptEngine afterRunScriptEngine;
	private SoapUIScriptEngine beforeRunScriptEngine;
	private Set<ProjectRunListener> runListeners = new HashSet<ProjectRunListener>();

	private Environment environment;
	protected EndpointSupport endpointSupport;

	protected final static Logger log = Logger.getLogger( WsdlProject.class );

	public WsdlProject() throws XmlException, IOException, SoapUIException
	{
		this( ( WorkspaceImpl )null );
	}

	public WsdlProject( String path ) throws XmlException, IOException, SoapUIException
	{
		this( path, ( WorkspaceImpl )null );
	}

	public WsdlProject( String projectFile, String projectPassword )
	{
		this( projectFile, null, true, true, null, projectPassword );
	}

	public WsdlProject( WorkspaceImpl workspace )
	{
		this( null, workspace, true );
	}

	public WsdlProject( String path, WorkspaceImpl workspace )
	{
		this( path, workspace, true );
	}

	public WsdlProject( String path, WorkspaceImpl workspace, boolean create )
	{
		this( path, workspace, create, true, null, null );
	}

	public WsdlProject( String path, WorkspaceImpl workspace, boolean create, boolean open, String tempName,
			String projectPassword )
	{
		super( null, workspace, "/project.gif" );

		this.workspace = workspace;
		this.path = path;
		this.projectPassword = projectPassword;
		endpointSupport = new EndpointSupport();

		for( ProjectListener listener : SoapUI.getListenerRegistry().getListeners( ProjectListener.class ) )
		{
			addProjectListener( listener );
		}

		for( ProjectRunListener listener : SoapUI.getListenerRegistry().getListeners( ProjectRunListener.class ) )
		{
			addProjectRunListener( listener );
		}

		try
		{
			if( path != null && open )
			{
				File file = new File( path.trim() );
				if( file.exists() )
				{
					try
					{
						loadProject( file.toURI().toURL() );
						lastModified = file.lastModified();
					}
					catch( MalformedURLException e )
					{
						SoapUI.logError( e );
						disabled = true;
					}
				}
				else
				{
					try
					{
						if( !PathUtils.isHttpPath( path ) )
							SoapUI.log.info( "File [" + file.getAbsolutePath() + "] does not exist, trying URL instead" );

						remote = true;
						loadProject( new URL( path ) );
					}
					catch( MalformedURLException e )
					{
						SoapUI.logError( e );
						disabled = true;
					}
				}
			}
		}
		catch( SoapUIException e )
		{
			SoapUI.logError( e );
			disabled = true;
		}
		finally
		{
			closedIcon = UISupport.createImageIcon( "/closedProject.gif" );
			remoteIcon = UISupport.createImageIcon( "/remoteProject.gif" );
			disabledIcon = UISupport.createImageIcon( "/disabledProject.gif" );
			openEncyptedIcon = UISupport.createImageIcon( "/openEncryptedProject.gif" );
			closedEncyptedIcon = UISupport.createImageIcon( "/closedEncryptedProject.gif" );

			this.open = open && !disabled && ( this.encrypted != -1 );

			if( projectDocument == null )
			{
				projectDocument = SoapuiProjectDocumentConfig.Factory.newInstance();
				setConfig( projectDocument.addNewSoapuiProject() );
				if( tempName != null || path != null )
					getConfig().setName( StringUtils.isNullOrEmpty( tempName ) ? getNameFromPath() : tempName );

				setPropertiesConfig( getConfig().addNewProperties() );
				wssContainer = new DefaultWssContainer( this, getConfig().addNewWssContainer() );
				// setResourceRoot("${projectDir}");
			}

			if( getConfig() != null )
			{
				endpointStrategy.init( this );
			}
			if( getSettings() != null )
			{
				setProjectRoot( path );
			}
			if( getConfig() != null && this.environment == null )
			{
				setActiveEnvironment( DefaultEnvironment.getInstance() );
			}

			addPropertyChangeListener( this );
		}
	}

	public boolean isRemote()
	{
		return remote;
	}

	public EndpointSupport getEndpointSupport()
	{
		return endpointSupport;
	}

	public void loadProject( URL file ) throws SoapUIException
	{
		try
		{
			UISupport.setHourglassCursor();

			UrlWsdlLoader loader = new UrlWsdlLoader( file.toString(), this );
			loader.setUseWorker( false );
			projectDocument = SoapuiProjectDocumentConfig.Factory.parse( loader.load() );

			// see if there is encoded data
			this.encrypted = checkForEncodedData( projectDocument.getSoapuiProject() );

			setConfig( projectDocument.getSoapuiProject() );

			// removed cached definitions if caching is disabled
			if( !getSettings().getBoolean( WsdlSettings.CACHE_WSDLS ) )
			{
				removeDefinitionCaches( projectDocument );
			}

			log.info( "Loaded project from [" + file.toString() + "]" );

			// TODO remove this after beta2 - this was for 4.0.0
			//			updateChecksToScans( getConfig() );

			try
			{
				int majorVersion = Integer
						.parseInt( projectDocument.getSoapuiProject().getSoapuiVersion().split( "\\." )[0] );
				if( majorVersion > Integer.parseInt( SoapUI.SOAPUI_VERSION.split( "\\." )[0] ) )
					log.warn( "Project '" + projectDocument.getSoapuiProject().getName() + "' is from a newer version ("
							+ projectDocument.getSoapuiProject().getSoapuiVersion() + ") of soapUI than this ("
							+ SoapUI.SOAPUI_VERSION + ") and parts of it may be incompatible or incorrect. "
							+ "Saving this project with this version of soapUI may cause it to function differently." );
			}
			catch( Exception e )
			{
			}

			if( !getConfig().isSetProperties() )
				getConfig().addNewProperties();

			setPropertiesConfig( getConfig().getProperties() );

			List<InterfaceConfig> interfaceConfigs = getConfig().getInterfaceList();
			for( InterfaceConfig config : interfaceConfigs )
			{
				AbstractInterface<?> iface = InterfaceFactoryRegistry.build( this, config );
				interfaces.add( iface );
			}

			List<TestSuiteConfig> testSuiteConfigs = getConfig().getTestSuiteList();
			for( TestSuiteConfig config : testSuiteConfigs )
			{
				testSuites.add( buildTestSuite( config ) );
			}

			List<MockServiceConfig> mockServiceConfigs = getConfig().getMockServiceList();
			for( MockServiceConfig config : mockServiceConfigs )
			{
				mockServices.add( new WsdlMockService( this, config ) );
			}

			if( !getConfig().isSetWssContainer() )
				getConfig().addNewWssContainer();

			wssContainer = new DefaultWssContainer( this, getConfig().getWssContainer() );

			endpointStrategy.init( this );

			setActiveEnvironment( DefaultEnvironment.getInstance() );

			if( !getConfig().isSetAbortOnError() )
				getConfig().setAbortOnError( false );

			// if( !getConfig().isSetFailOnErrors() )
			// getConfig().setFailOnErrors( true );

			if( !getConfig().isSetRunType() )
				getConfig().setRunType( TestSuiteRunTypesConfig.SEQUENTIAL );

			afterLoad();
		}
		catch( Exception e )
		{
			if( e instanceof XmlException )
			{
				XmlException xe = ( XmlException )e;
				XmlError error = xe.getError();
				if( error != null )
					System.err.println( "Error at line " + error.getLine() + ", column " + error.getColumn() );
			}

			if( e instanceof RestConversionException )
			{
				log.error( "Project file needs to be updated manually, please reload the project." );
				throw new SoapUIException( "Failed to load project from file [" + file.toString() + "]", e );
			}

			e.printStackTrace();
			throw new SoapUIException( "Failed to load project from file [" + file.toString() + "]", e );
		}
		finally
		{
			UISupport.resetCursor();
		}
	}

	public Environment getActiveEnvironment()
	{
		return environment;
	}

	public void setActiveEnvironment( Environment environment )
	{
		if( !environment.equals( this.environment ) )
		{
			this.environment = environment;
			getConfig().setActiveEnvironment( environment.getName() );
			fireEnvironmentSwitched( environment );
		}
	}

	public boolean isEnvironmentMode()
	{
		return false;
	}

	protected WsdlTestSuite buildTestSuite( TestSuiteConfig config )
	{
		return new WsdlTestSuite( this, config );
	}

	public boolean isWrongPasswordSupplied()
	{
		return wrongPasswordSupplied;
	}

	/**
	 * Decode encrypted data and restore user/pass
	 * 
	 * @param soapuiProject
	 * @return 0 - not encrypted, 1 - successfull decryption , -1 error while
	 *         decrypting, bad password, no password.
	 * @throws IOException
	 * @throws GeneralSecurityException
	 * @author robert nemet
	 */
	protected int checkForEncodedData( ProjectConfig soapuiProject ) throws IOException, GeneralSecurityException
	{

		byte[] encryptedContent = soapuiProject.getEncryptedContent();
		char[] password = null;

		// no encrypted data then go back
		if( encryptedContent == null || encryptedContent.length == 0 )
			return 0;

		String projectPassword = null;
		if( workspace != null )
		{
			projectPassword = workspace.getProjectPassword( soapuiProject.getName() );
		}
		else
		{
			projectPassword = this.projectPassword;
		}
		if( projectPassword == null )
		{
			password = UISupport.promptPassword( "Enter Password:", soapuiProject.getName() );
		}
		else
		{
			password = projectPassword.toCharArray();
		}
		byte[] data = null;
		// no pass go back.
		if( password == null )
		{
			return -1;
		}

		try
		{
			data = OpenSSL.decrypt( "des3", password, encryptedContent );
		}
		catch( Exception e )
		{
			SoapUI.logError( e );
			return -1;
		}

		String decryptedData = new String( data, "UTF-8" );

		if( decryptedData != null )
		{
			if( decryptedData.length() > 0 )
			{
				try
				{
					// projectDocument.getSoapuiProject().set(
					// XmlObject.Factory.parse( decryptedData ) );
					projectDocument.getSoapuiProject().set( XmlUtils.createXmlObject( decryptedData ) );
					wrongPasswordSupplied = false;
				}
				catch( XmlException e )
				{
					UISupport.showErrorMessage( "Wrong password. Project needs to be reloaded." );
					wrongPasswordSupplied = true;
					getWorkspace().clearProjectPassword( soapuiProject.getName() );
					return -1;
				}
			}
		}
		else
		{
			UISupport.showErrorMessage( "Wrong project password" );
			wrongPasswordSupplied = true;
			getWorkspace().clearProjectPassword( soapuiProject.getName() );
			return -1;
		}
		projectDocument.getSoapuiProject().setEncryptedContent( null );
		return 1;
	}

	@Override
	public void afterLoad()
	{
		super.afterLoad();

		try
		{
			ProjectListener[] a = projectListeners.toArray( new ProjectListener[projectListeners.size()] );

			for( int c = 0; c < a.length; c++ )
			{
				a[c].afterLoad( this );
			}

			runAfterLoadScript();
		}
		catch( Exception e )
		{
			SoapUI.logError( e );
		}
	}

	protected void setProjectRoot( String path )
	{
		if( path != null && projectDocument != null )
		{
			int ix = path.lastIndexOf( File.separatorChar );
			if( ix > 0 )
				getSettings().setString( ProjectSettings.PROJECT_ROOT, path.substring( 0, ix ) );
		}
	}

	public void setResourceRoot( String resourceRoot )
	{
		String old = getResourceRoot();

		getConfig().setResourceRoot( resourceRoot );
		notifyPropertyChanged( RESOURCE_ROOT_PROPERTY, old, resourceRoot );
	}

	public String getResourceRoot()
	{
		if( !getConfig().isSetResourceRoot() )
			getConfig().setResourceRoot( "" );

		return getConfig().getResourceRoot();
	}

	@Override
	public ImageIcon getIcon()
	{
		if( isDisabled() )
			return disabledIcon;
		else if( getEncrypted() != 0 )
		{
			if( isOpen() )
			{
				return openEncyptedIcon;
			}
			else
			{
				return closedEncyptedIcon;
			}
		}
		else if( !isOpen() )
			return closedIcon;
		else if( isRemote() )
			return remoteIcon;
		else
			return super.getIcon();
	}

	private String getNameFromPath()
	{
		int ix = path.lastIndexOf( isRemote() ? '/' : File.separatorChar );
		String name = ix == -1 ? path : path.substring( ix + 1 );
		return name;
	}

	@Override
	public String getDescription()
	{
		if( isOpen() )
			return super.getDescription();

		String name = getName();

		if( isDisabled() )
			name += " - disabled [" + getPath() + "]";
		else
			name += " - closed [" + getPath() + "]";

		return name;
	}

	public WorkspaceImpl getWorkspace()
	{
		return workspace;
	}

	public AbstractInterface<?> getInterfaceAt( int index )
	{
		return interfaces.get( index );
	}

	public AbstractInterface<?> getInterfaceByName( String interfaceName )
	{
		return ( AbstractInterface<?> )getWsdlModelItemByName( interfaces, interfaceName );
	}

	public AbstractInterface<?> getInterfaceByTechnicalId( String technicalId )
	{
		for( int c = 0; c < getInterfaceCount(); c++ )
		{
			if( getInterfaceAt( c ).getTechnicalId().equals( technicalId ) )
				return getInterfaceAt( c );
		}

		return null;
	}

	public int getInterfaceCount()
	{
		return interfaces.size();
	}

	public String getPath()
	{
		return path;
	}

	public boolean save() throws IOException
	{
		return save( null );
	}

	public boolean save( String folder ) throws IOException
	{
		if( !isOpen() || isDisabled() || isRemote() )
			return true;

		if( path == null || isRemote() )
		{
			path = StringUtils.createFileName2( getName(), '-' ) + "-soapui-project.xml";
			if( folder != null )
			{
				path = folder + File.separatorChar + path;
			}

			File file = null;

			while( file == null
					|| ( file.exists() && !UISupport.confirm( "File [" + file.getName() + "] exists, overwrite?",
							"Overwrite File?" ) ) )
			{
				file = UISupport.getFileDialogs().saveAs( this, "Save project " + getName(), ".xml", "XML Files (*.xml)",
						new File( path ) );
				if( file == null )
					return false;
			}

			path = file.getAbsolutePath();
		}

		File projectFile = new File( path );

		while( projectFile.exists() && !projectFile.canWrite() )
		{
			if( UISupport.confirm( "Project file [" + path + "] can not be written to, save to new file?", "Save Project" ) )
			{
				projectFile = UISupport.getFileDialogs().saveAs( this, "Save project " + getName(), ".xml",
						"XML Files (*.xml)", projectFile );

				if( projectFile == null )
					return false;

				path = projectFile.getAbsolutePath();
			}
			else
				return false;
		}

		// check modified
		if( projectFile.exists() && lastModified != 0 && lastModified < projectFile.lastModified() )
		{
			if( !UISupport.confirm( "Project file for [" + getName() + "] has been modified externally, overwrite?",
					"Save Project" ) )
				return false;
		}

		if( projectFile.exists() && getSettings().getBoolean( UISettings.CREATE_BACKUP ) )
		{
			createBackup( projectFile );
		}

		return saveIn( projectFile );
	}

	public boolean saveBackup() throws IOException
	{
		File projectFile;
		if( path == null || isRemote() )
		{
			projectFile = new File( StringUtils.createFileName2( getName(), '-' ) + "-soapui-project.xml" );
		}
		else
		{
			projectFile = new File( path );
		}
		File backupFile = getBackupFile( projectFile );
		return saveIn( backupFile );
	}

	public boolean saveIn( File projectFile ) throws IOException
	{
		long size = 0;

		beforeSave();
		// work with copy beacuse we do not want to change working project while
		// working with it
		// if user choose save project, save all etc.
		SoapuiProjectDocumentConfig projectDocument = ( SoapuiProjectDocumentConfig )this.projectDocument.copy();

		// check for caching
		if( !getSettings().getBoolean( WsdlSettings.CACHE_WSDLS ) )
		{
			// no caching -> create copy and remove definition cachings
			removeDefinitionCaches( projectDocument );
		}

		// remove project root
		XmlBeansSettingsImpl tempSettings = new XmlBeansSettingsImpl( this, null, projectDocument.getSoapuiProject()
				.getSettings() );
		tempSettings.clearSetting( ProjectSettings.PROJECT_ROOT );

		// check for encryption
		String passwordForEncryption = getSettings().getString( ProjectSettings.SHADOW_PASSWORD, null );

		// if it has encryptedContend that means it is not decrypted corectly( bad
		// password, etc ), so do not encrypt it again.
		if( projectDocument.getSoapuiProject().getEncryptedContent() == null )
		{
			if( passwordForEncryption != null )
			{
				if( passwordForEncryption.length() > 1 )
				{
					// we have password so do encryption
					try
					{
						String data = getConfig().xmlText();
						byte[] encrypted = OpenSSL.encrypt( "des3", passwordForEncryption.toCharArray(), data.getBytes() );
						projectDocument.getSoapuiProject().setEncryptedContent( encrypted );
						projectDocument.getSoapuiProject().setInterfaceArray( null );
						projectDocument.getSoapuiProject().setTestSuiteArray( null );
						projectDocument.getSoapuiProject().setMockServiceArray( null );
						projectDocument.getSoapuiProject().unsetWssContainer();
						projectDocument.getSoapuiProject().unsetSettings();
						projectDocument.getSoapuiProject().unsetProperties();

					}
					catch( GeneralSecurityException e )
					{
						UISupport.showErrorMessage( "Encryption Error" );
					}
				}
				else
				{
					// no password no encryption.
					projectDocument.getSoapuiProject().setEncryptedContent( null );
				}
			}
		}
		// end of encryption.

		XmlOptions options = new XmlOptions();
		if( SoapUI.getSettings().getBoolean( WsdlSettings.PRETTY_PRINT_PROJECT_FILES ) )
			options.setSavePrettyPrint();

		projectDocument.getSoapuiProject().setSoapuiVersion( SoapUI.SOAPUI_VERSION );

		try
		{
			File tempFile = File.createTempFile( "project-temp-", ".xml", projectFile.getParentFile() );

			// save once to make sure it can be saved
			FileOutputStream tempOut = new FileOutputStream( tempFile );
			projectDocument.save( tempOut, options );
			tempOut.close();

			if( getSettings().getBoolean( UISettings.LINEBREAK ) )
			{
				normalizeLineBreak( projectFile, tempFile );
			}
			else
			{
				// now save it for real
				FileOutputStream projectOut = new FileOutputStream( projectFile );
				projectDocument.save( projectOut, options );
				projectOut.close();
				// delete tempFile here so we have it as backup in case second save
				// fails
				if( !tempFile.delete() )
				{
					SoapUI.getErrorLog().warn( "Failed to delete temporary project file; " + tempFile.getAbsolutePath() );
					tempFile.deleteOnExit();
				}
			}

			// delete tempFile here so we have it as backup in case second save fails
			if( !tempFile.delete() )
			{
				SoapUI.getErrorLog().warn( "Failed to delete temporary project file; " + tempFile.getAbsolutePath() );
				tempFile.deleteOnExit();
			}

			size = projectFile.length();
		}
		catch( Throwable t )
		{
			SoapUI.logError( t );
			UISupport.showErrorMessage( "Failed to save project [" + getName() + "]: " + t.toString() );
			return false;
		}

		lastModified = projectFile.lastModified();
		log.info( "Saved project [" + getName() + "] to [" + projectFile.getAbsolutePath() + " - " + size + " bytes" );
		setProjectRoot( getPath() );
		return true;
	}

	private static void normalizeLineBreak( File target, File tmpFile ) throws IOException
	{
		FileReader fr = new FileReader( tmpFile );
		BufferedReader in = new BufferedReader( fr );
		FileWriter fw = new FileWriter( target );
		BufferedWriter out = new BufferedWriter( fw );
		String line = "";
		while( ( line = in.readLine() ) != null )
		{
			out.write( line );
			out.newLine();
			out.flush();
		}
		out.close();
		fw.close();
		in.close();
		fr.close();
	}

	public void beforeSave()
	{
		try
		{
			ProjectListener[] a = projectListeners.toArray( new ProjectListener[projectListeners.size()] );

			for( int c = 0; c < a.length; c++ )
			{
				a[c].beforeSave( this );
			}

			runBeforeSaveScript();
		}
		catch( Exception e )
		{
			SoapUI.logError( e );
		}

		// notify
		for( AbstractInterface<?> iface : interfaces )
			iface.beforeSave();

		for( WsdlTestSuite testSuite : testSuites )
			testSuite.beforeSave();

		for( WsdlMockService mockService : mockServices )
			mockService.beforeSave();

		endpointStrategy.onSave();
	}

	protected void createBackup( File projectFile ) throws IOException
	{
		File backupFile = getBackupFile( projectFile );
		log.info( "Backing up [" + projectFile + "] to [" + backupFile + "]" );
		Tools.copyFile( projectFile, backupFile, true );
	}

	protected File getBackupFile( File projectFile )
	{
		String backupFolderName = getSettings().getString( UISettings.BACKUP_FOLDER, "" );

		File backupFolder = new File( backupFolderName );
		if( !backupFolder.isAbsolute() )
		{
			backupFolder = new File( projectFile.getParentFile(), backupFolderName );
		}

		if( !backupFolder.exists() )
			backupFolder.mkdirs();

		File backupFile = new File( backupFolder, projectFile.getName() + ".backup" );
		return backupFile;
	}

	protected void removeDefinitionCaches( SoapuiProjectDocumentConfig config )
	{
		for( InterfaceConfig ifaceConfig : config.getSoapuiProject().getInterfaceList() )
		{
			if( ifaceConfig.isSetDefinitionCache() )
			{
				log.info( "Removing definition cache from interface [" + ifaceConfig.getName() + "]" );
				ifaceConfig.unsetDefinitionCache();
			}
		}
	}

	public AbstractInterface<?> addNewInterface( String name, String type )
	{
		AbstractInterface<?> iface = ( AbstractInterface<?> )InterfaceFactoryRegistry.createNew( this, type, name );
		if( iface != null )
		{
			iface.getConfig().setType( type );

			interfaces.add( iface );
			fireInterfaceAdded( iface );
		}

		return iface;
	}

	public void addProjectListener( ProjectListener listener )
	{
		projectListeners.add( listener );
	}

	public void removeProjectListener( ProjectListener listener )
	{
		projectListeners.remove( listener );
	}

	public void fireInterfaceAdded( AbstractInterface<?> iface )
	{
		ProjectListener[] a = projectListeners.toArray( new ProjectListener[projectListeners.size()] );

		for( int c = 0; c < a.length; c++ )
		{
			a[c].interfaceAdded( iface );
		}
	}

	public void fireInterfaceRemoved( AbstractInterface<?> iface )
	{
		ProjectListener[] a = projectListeners.toArray( new ProjectListener[projectListeners.size()] );

		for( int c = 0; c < a.length; c++ )
		{
			a[c].interfaceRemoved( iface );
		}
	}

	public void fireInterfaceUpdated( AbstractInterface<?> iface )
	{
		ProjectListener[] a = projectListeners.toArray( new ProjectListener[projectListeners.size()] );

		for( int c = 0; c < a.length; c++ )
		{
			a[c].interfaceUpdated( iface );
		}
	}

	public void fireTestSuiteAdded( WsdlTestSuite testSuite )
	{
		ProjectListener[] a = projectListeners.toArray( new ProjectListener[projectListeners.size()] );

		for( int c = 0; c < a.length; c++ )
		{
			a[c].testSuiteAdded( testSuite );
		}
	}

	private void fireTestSuiteMoved( WsdlTestSuite testCase, int ix, int offset )
	{
		ProjectListener[] a = projectListeners.toArray( new ProjectListener[projectListeners.size()] );

		for( int c = 0; c < a.length; c++ )
		{
			a[c].testSuiteMoved( testCase, ix, offset );
		}
	}

	public void fireTestSuiteRemoved( WsdlTestSuite testSuite )
	{
		ProjectListener[] a = projectListeners.toArray( new ProjectListener[projectListeners.size()] );

		for( int c = 0; c < a.length; c++ )
		{
			a[c].testSuiteRemoved( testSuite );
		}
	}

	public void fireMockServiceAdded( WsdlMockService mockService )
	{
		ProjectListener[] a = projectListeners.toArray( new ProjectListener[projectListeners.size()] );

		for( int c = 0; c < a.length; c++ )
		{
			a[c].mockServiceAdded( mockService );
		}
	}

	public void fireMockServiceRemoved( WsdlMockService mockService )
	{
		ProjectListener[] a = projectListeners.toArray( new ProjectListener[projectListeners.size()] );

		for( int c = 0; c < a.length; c++ )
		{
			a[c].mockServiceRemoved( mockService );
		}
	}

	public void fireEnvironmentAdded( Environment env )
	{
		ProjectListener[] a = projectListeners.toArray( new ProjectListener[projectListeners.size()] );

		for( int c = 0; c < a.length; c++ )
		{
			a[c].environmentAdded( env );
		}
	}

	private void fireEnvironmentSwitched( Environment environment )
	{
		ProjectListener[] a = projectListeners.toArray( new ProjectListener[projectListeners.size()] );

		for( int c = 0; c < a.length; c++ )
		{
			a[c].environmentSwitched( environment );
		}
	}

	public void fireEnvironmentRemoved( Environment env, int index )
	{
		ProjectListener[] a = projectListeners.toArray( new ProjectListener[projectListeners.size()] );

		for( int c = 0; c < a.length; c++ )
		{
			a[c].environmentRemoved( env, index );
		}
	}

	public void fireEnvironmentRenamed( Environment env, String oldName, String newName )
	{
		ProjectListener[] a = projectListeners.toArray( new ProjectListener[projectListeners.size()] );

		for( int c = 0; c < a.length; c++ )
		{
			a[c].environmentRenamed( env, oldName, newName );
		}
	}

	public void removeInterface( AbstractInterface<?> iface )
	{
		int ix = interfaces.indexOf( iface );
		interfaces.remove( ix );
		try
		{
			fireInterfaceRemoved( iface );
		}
		finally
		{
			iface.release();
			getConfig().removeInterface( ix );
		}
	}

	public void removeTestSuite( WsdlTestSuite testSuite )
	{
		int ix = testSuites.indexOf( testSuite );
		testSuites.remove( ix );

		try
		{
			fireTestSuiteRemoved( testSuite );
		}
		finally
		{
			testSuite.release();
			getConfig().removeTestSuite( ix );
		}
	}

	public void firePropertyValueChanged( Property property )
	{
		EnvironmentListener[] a = environmentListeners.toArray( new EnvironmentListener[environmentListeners.size()] );

		for( int c = 0; c < a.length; c++ )
		{
			a[c].propertyValueChanged( property );
		}
	}

	public boolean isDisabled()
	{
		return disabled;
	}

	public int getTestSuiteCount()
	{
		return testSuites.size();
	}

	public WsdlTestSuite getTestSuiteAt( int index )
	{
		return testSuites.get( index );
	}

	public WsdlTestSuite getTestSuiteByName( String testSuiteName )
	{
		return ( WsdlTestSuite )getWsdlModelItemByName( testSuites, testSuiteName );
	}

	public WsdlTestSuite addNewTestSuite( String name )
	{
		WsdlTestSuite testSuite = buildTestSuite( getConfig().addNewTestSuite() );
		testSuite.setName( name );
		testSuites.add( testSuite );
		fireTestSuiteAdded( testSuite );

		return testSuite;
	}

	public boolean isCacheDefinitions()
	{
		return getSettings().getBoolean( WsdlSettings.CACHE_WSDLS );
	}

	public void setCacheDefinitions( boolean cacheDefinitions )
	{
		getSettings().setBoolean( WsdlSettings.CACHE_WSDLS, cacheDefinitions );
	}

	public boolean saveAs( String fileName ) throws IOException
	{
		if( !isOpen() || isDisabled() )
			return false;

		String oldPath = path;
		path = fileName;
		boolean result = save();
		if( !result )
			path = oldPath;
		else
			remote = false;

		setProjectRoot( path );

		return result;
	}

	@Override
	public void release()
	{
		super.release();

		if( isOpen() )
		{
			endpointStrategy.release();

			for( WsdlTestSuite testSuite : testSuites )
				testSuite.release();

			for( WsdlMockService mockService : mockServices )
				mockService.release();

			for( AbstractInterface<?> iface : interfaces )
				iface.release();

			if( wssContainer != null )
			{
				wssContainer.release();
				wssContainer = null;
			}
		}

		projectListeners.clear();

		environmentListeners.clear();

		if( afterLoadScriptEngine != null )
			afterLoadScriptEngine.release();

		if( beforeSaveScriptEngine != null )
			beforeSaveScriptEngine.release();
	}

	public WsdlMockService addNewMockService( String name )
	{
		WsdlMockService mockService = new WsdlMockService( this, getConfig().addNewMockService() );
		mockService.setName( name );
		mockServices.add( mockService );
		fireMockServiceAdded( mockService );

		return mockService;
	}

	public WsdlMockService getMockServiceAt( int index )
	{
		return mockServices.get( index );
	}

	public WsdlMockService getMockServiceByName( String mockServiceName )
	{
		return ( WsdlMockService )getWsdlModelItemByName( mockServices, mockServiceName );
	}

	public int getMockServiceCount()
	{
		return mockServices.size();
	}

	public void removeMockService( WsdlMockService mockService )
	{
		int ix = mockServices.indexOf( mockService );
		mockServices.remove( ix );

		try
		{
			fireMockServiceRemoved( mockService );
		}
		finally
		{
			mockService.release();
			getConfig().removeMockService( ix );
		}
	}

	public List<TestSuite> getTestSuiteList()
	{
		return new ArrayList<TestSuite>( testSuites );
	}

	public List<MockService> getMockServiceList()
	{
		return new ArrayList<MockService>( mockServices );
	}

	public List<Interface> getInterfaceList()
	{
		return new ArrayList<Interface>( interfaces );
	}

	public Map<String, Interface> getInterfaces()
	{
		Map<String, Interface> result = new HashMap<String, Interface>();
		for( Interface iface : interfaces )
			result.put( iface.getName(), iface );

		return result;
	}

	public Map<String, TestSuite> getTestSuites()
	{
		Map<String, TestSuite> result = new HashMap<String, TestSuite>();
		for( TestSuite iface : testSuites )
			result.put( iface.getName(), iface );

		return result;
	}

	public Map<String, MockService> getMockServices()
	{
		Map<String, MockService> result = new HashMap<String, MockService>();
		for( MockService mockService : mockServices )
			result.put( mockService.getName(), mockService );

		return result;
	}

	public void reload() throws SoapUIException
	{
		reload( path );
	}

	public void reload( String path ) throws SoapUIException
	{
		this.path = path;
		getWorkspace().reloadProject( this );
	}

	public boolean hasNature( String natureId )
	{
		Settings projectSettings = getSettings();
		String projectNature = projectSettings.getString( ProjectSettings.PROJECT_NATURE, null );
		return natureId.equals( projectNature );
	}

	public AbstractInterface<?> importInterface( AbstractInterface<?> iface, boolean importEndpoints, boolean createCopy )
	{
		iface.beforeSave();

		InterfaceConfig ifaceConfig = ( InterfaceConfig )iface.getConfig().copy();
		ifaceConfig = ( InterfaceConfig )getConfig().addNewInterface().set( ifaceConfig );

		AbstractInterface<?> imported = InterfaceFactoryRegistry.build( this, ifaceConfig );
		interfaces.add( imported );

		if( iface.getProject() != this && importEndpoints )
		{
			endpointStrategy.importEndpoints( iface );
		}

		if( createCopy )
			ModelSupport.unsetIds( imported );

		imported.afterLoad();
		fireInterfaceAdded( imported );

		return imported;
	}

	public WsdlTestSuite importTestSuite( WsdlTestSuite testSuite, String name, int index, boolean createCopy,
			String description )
	{
		testSuite.beforeSave();
		TestSuiteConfig testSuiteConfig = index == -1 ? ( TestSuiteConfig )getConfig().addNewTestSuite().set(
				testSuite.getConfig().copy() ) : ( TestSuiteConfig )getConfig().insertNewTestSuite( index ).set(
				testSuite.getConfig().copy() );

		testSuiteConfig.setName( name );

		if( createCopy )
		{
			for( TestCaseConfig testCaseConfig : testSuiteConfig.getTestCaseList() )
			{
				testCaseConfig.setSecurityTestArray( new SecurityTestConfig[0] );
			}
		}

		WsdlTestSuite oldTestSuite = testSuite;
		testSuite = buildTestSuite( testSuiteConfig );

		if( description != null )
			testSuite.setDescription( description );

		if( index == -1 )
			testSuites.add( testSuite );
		else
			testSuites.add( index, testSuite );

		if( createCopy )
			ModelSupport.unsetIds( testSuite );

		testSuite.afterLoad();

		if( createCopy )
		{
			testSuite.afterCopy( oldTestSuite );
		}

		fireTestSuiteAdded( testSuite );

		resolveImportedTestSuite( testSuite );

		return testSuite;
	}

	public WsdlMockService importMockService( WsdlMockService mockService, String name, boolean createCopy,
			String description )
	{
		mockService.beforeSave();
		MockServiceConfig mockServiceConfig = ( MockServiceConfig )getConfig().addNewMockService().set(
				mockService.getConfig().copy() );
		mockServiceConfig.setName( name );
		if( mockServiceConfig.isSetId() && createCopy )
			mockServiceConfig.unsetId();
		mockService = new WsdlMockService( this, mockServiceConfig );
		mockService.setDescription( description );
		mockServices.add( mockService );
		if( createCopy )
			ModelSupport.unsetIds( mockService );

		mockService.afterLoad();

		fireMockServiceAdded( mockService );

		return mockService;
	}

	public EndpointStrategy getEndpointStrategy()
	{
		return endpointStrategy;
	}

	public boolean isOpen()
	{
		return open;
	}

	public List<? extends ModelItem> getChildren()
	{
		ArrayList<ModelItem> list = new ArrayList<ModelItem>();
		list.addAll( getInterfaceList() );
		list.addAll( getTestSuiteList() );
		list.addAll( getMockServiceList() );
		return list;
	}

	public void setAfterLoadScript( String script )
	{
		String oldScript = getAfterLoadScript();

		if( !getConfig().isSetAfterLoadScript() )
			getConfig().addNewAfterLoadScript();

		getConfig().getAfterLoadScript().setStringValue( script );
		if( afterLoadScriptEngine != null )
			afterLoadScriptEngine.setScript( script );

		notifyPropertyChanged( AFTER_LOAD_SCRIPT_PROPERTY, oldScript, script );
	}

	public String getAfterLoadScript()
	{
		return getConfig().isSetAfterLoadScript() ? getConfig().getAfterLoadScript().getStringValue() : null;
	}

	public void setBeforeSaveScript( String script )
	{
		String oldScript = getBeforeSaveScript();

		if( !getConfig().isSetBeforeSaveScript() )
			getConfig().addNewBeforeSaveScript();

		getConfig().getBeforeSaveScript().setStringValue( script );
		if( beforeSaveScriptEngine != null )
			beforeSaveScriptEngine.setScript( script );

		notifyPropertyChanged( BEFORE_SAVE_SCRIPT_PROPERTY, oldScript, script );
	}

	public String getBeforeSaveScript()
	{
		return getConfig().isSetBeforeSaveScript() ? getConfig().getBeforeSaveScript().getStringValue() : null;
	}

	public Object runAfterLoadScript() throws Exception
	{
		String script = getAfterLoadScript();
		if( StringUtils.isNullOrEmpty( script ) )
			return null;

		if( afterLoadScriptEngine == null )
		{
			afterLoadScriptEngine = SoapUIScriptEngineRegistry.create( this );
			afterLoadScriptEngine.setScript( script );
		}

		afterLoadScriptEngine.setVariable( "context", context );
		afterLoadScriptEngine.setVariable( "project", this );
		afterLoadScriptEngine.setVariable( "log", SoapUI.ensureGroovyLog() );
		return afterLoadScriptEngine.run();
	}

	public Object runBeforeSaveScript() throws Exception
	{
		String script = getBeforeSaveScript();
		if( StringUtils.isNullOrEmpty( script ) )
			return null;

		if( beforeSaveScriptEngine == null )
		{
			beforeSaveScriptEngine = SoapUIScriptEngineRegistry.create( this );
			beforeSaveScriptEngine.setScript( script );
		}

		beforeSaveScriptEngine.setVariable( "context", context );
		beforeSaveScriptEngine.setVariable( "project", this );
		beforeSaveScriptEngine.setVariable( "log", SoapUI.ensureGroovyLog() );
		return beforeSaveScriptEngine.run();
	}

	public PropertyExpansionContext getContext()
	{
		return context;
	}

	public DefaultWssContainer getWssContainer()
	{
		return wssContainer;
	}

	@Override
	public void resolve( ResolveContext<?> context )
	{
		super.resolve( context );

		wssContainer.resolve( context );
	}

	public PropertyExpansion[] getPropertyExpansions()
	{
		List<PropertyExpansion> result = new ArrayList<PropertyExpansion>();

		result.addAll( Arrays.asList( wssContainer.getPropertyExpansions() ) );
		// result.addAll(Arrays.asList(databaseConnectionContainer.
		// getPropertyExpansions()));

		return result.toArray( new PropertyExpansion[result.size()] );

	}

	@Override
	protected void addExternalDependencies( List<ExternalDependency> dependencies )
	{
		super.addExternalDependencies( dependencies );
		wssContainer.addExternalDependency( dependencies );
	}

	public String getShadowPassword()
	{
		projectPassword = getSettings() == null ? projectPassword : getSettings().getString(
				ProjectSettings.SHADOW_PASSWORD, null );
		return projectPassword;
	}

	public void setShadowPassword( String password )
	{
		String oldPassword = getSettings().getString( ProjectSettings.SHADOW_PASSWORD, null );
		getSettings().setString( ProjectSettings.SHADOW_PASSWORD, password );
		notifyPropertyChanged( "projectPassword", oldPassword, password );
	}

	public String getHermesConfig()
	{
		hermesConfig = getSettings() == null ? hermesConfig : resolveHermesConfig();
		return hermesConfig;
	}

	private String resolveHermesConfig()
	{
		String hermesConfigProperty = getSettings().getString( ProjectSettings.HERMES_CONFIG, null );
		if( hermesConfigProperty != null && !hermesConfigProperty.equals( "" ) )
		{
			return hermesConfigProperty;
		}
		else if( System.getenv( "HERMES_CONFIG" ) != null )
		{
			return System.getenv( "HERMES_CONFIG" );
		}
		else
		{
			return "${#System#user.home}\\.hermes";
		}

	}

	public void setHermesConfig( String hermesConfigPath )
	{
		String oldHermesConfigPath = getSettings().getString( ProjectSettings.HERMES_CONFIG, null );
		getSettings().setString( ProjectSettings.HERMES_CONFIG, hermesConfigPath );
		notifyPropertyChanged( "hermesConfig", oldHermesConfigPath, hermesConfigPath );

	}

	public void inspect()
	{

		if( !isOpen() )
			return;

		byte data[] = projectDocument.getSoapuiProject().getEncryptedContent();
		if( data != null && data.length > 0 )
		{
			try
			{
				reload();
			}
			catch( SoapUIException e )
			{
				e.printStackTrace();
			}
		}
	}

	public int getEncrypted()
	{
		return this.encrypted;
	}

	public int setEncrypted( int code )
	{
		return this.encrypted = code;
	}

	public void propertyChange( PropertyChangeEvent evt )
	{
		if( "projectPassword".equals( evt.getPropertyName() ) )
		{
			if( encrypted == 0 & ( evt.getOldValue() == null || ( ( String )evt.getOldValue() ).length() == 0 ) )
			{
				encrypted = 1;
			}
			if( encrypted == 1 & ( evt.getNewValue() == null || ( ( String )evt.getNewValue() ).length() == 0 ) )
			{
				encrypted = 0;
			}

			if( SoapUI.getNavigator() != null )
				SoapUI.getNavigator().repaint();
		}
	}

	public SoapuiProjectDocumentConfig getProjectDocument()
	{
		return projectDocument;
	}

	public int getInterfaceCount( String type )
	{
		int result = 0;

		for( AbstractInterface<?> iface : interfaces )
		{
			if( iface.getType().equals( type ) )
				result++ ;
		}

		return result;
	}

	public List<AbstractInterface<?>> getInterfaces( String type )
	{
		ArrayList<AbstractInterface<?>> result = new ArrayList<AbstractInterface<?>>();

		for( AbstractInterface<?> iface : interfaces )
		{
			if( iface.getType().equals( type ) )
				result.add( iface );
		}

		return result;
	}

	public void importTestSuite( File file )
	{
		if( !file.exists() )
		{
			UISupport.showErrorMessage( "Error loading test case " );
			return;
		}

		TestSuiteDocumentConfig newTestSuiteConfig = null;

		try
		{
			newTestSuiteConfig = TestSuiteDocumentConfig.Factory.parse( file );
		}
		catch( Exception e )
		{
			SoapUI.logError( e );
		}

		if( newTestSuiteConfig == null )
		{
			UISupport.showErrorMessage( "Not valid test case xml" );
		}
		else
		{
			TestSuiteConfig config = ( TestSuiteConfig )projectDocument.getSoapuiProject().addNewTestSuite()
					.set( newTestSuiteConfig.getTestSuite() );
			WsdlTestSuite testSuite = buildTestSuite( config );

			ModelSupport.unsetIds( testSuite );
			testSuite.afterLoad();

			/*
			 * security test keeps reference to test step by id, which gets changed
			 * during importing, so old values needs to be rewritten to new ones.
			 * 
			 * Create tarnsition table ( old id , new id ) and use it to replace
			 * all old ids in new imported test case.
			 * 
			 * Here needs to be done for all test cases separatly.
			 */
			for( int cnt2 = 0; cnt2 < config.getTestCaseList().size(); cnt2++ )
			{
				TestCaseConfig newTestCase = config.getTestCaseList().get( cnt2 );
				TestCaseConfig importTestCaseConfig = newTestSuiteConfig.getTestSuite().getTestCaseList().get( cnt2 );
				LinkedHashMap<String, String> oldNewIds = new LinkedHashMap<String, String>();
				for( int cnt = 0; cnt < importTestCaseConfig.getTestStepList().size(); cnt++ )
					oldNewIds.put( importTestCaseConfig.getTestStepList().get( cnt ).getId(), newTestCase.getTestStepList()
							.get( cnt ).getId() );

				for( SecurityTestConfig scan : newTestCase.getSecurityTestList() )
					for( TestStepSecurityTestConfig secStepConfig : scan.getTestStepSecurityTestList() )
						if( oldNewIds.containsKey( secStepConfig.getTestStepId() ) )
							secStepConfig.setTestStepId( oldNewIds.get( secStepConfig.getTestStepId() ) );

			}
			testSuites.add( testSuite );
			fireTestSuiteAdded( testSuite );

			resolveImportedTestSuite( testSuite );
		}
	}

	private void resolveImportedTestSuite( WsdlTestSuite testSuite )
	{
		ResolveDialog resolver = new ResolveDialog( "Validate TestSuite", "Checks TestSuite for inconsistencies", null );
		resolver.setShowOkMessage( false );
		resolver.resolve( testSuite );
	}

	/**
	 * @see com.eviware.soapui.impl.WsdlInterfaceFactory.importWsdl
	 * @deprecated
	 */

	public WsdlInterface[] importWsdl( String url, boolean createRequests ) throws SoapUIException
	{
		return WsdlInterfaceFactory.importWsdl( this, url, createRequests );
	}

	/**
	 * @see com.eviware.soapui.impl.WsdlInterfaceFactory.importWsdl
	 * @deprecated see WsdlInterfaceFactory
	 */

	public WsdlInterface[] importWsdl( String url, boolean createRequests, WsdlLoader wsdlLoader )
			throws SoapUIException
	{
		return WsdlInterfaceFactory.importWsdl( this, url, createRequests, null, wsdlLoader );
	}

	/**
	 * @see com.eviware.soapui.impl.WsdlInterfaceFactory.importWsdl
	 * @deprecated see WsdlInterfaceFactory
	 */

	public WsdlInterface[] importWsdl( String url, boolean createRequests, QName bindingName, WsdlLoader wsdlLoader )
			throws SoapUIException
	{
		return WsdlInterfaceFactory.importWsdl( this, url, createRequests, bindingName, wsdlLoader );
	}

	public void setDefaultScriptLanguage( String id )
	{
		getConfig().setDefaultScriptLanguage( id );
	}

	public String getDefaultScriptLanguage()
	{
		if( getConfig().isSetDefaultScriptLanguage() )
			return getConfig().getDefaultScriptLanguage();
		else
			return SoapUIScriptEngineRegistry.DEFAULT_SCRIPT_ENGINE_ID;
	}

	public int getIndexOfTestSuite( TestSuite testSuite )
	{
		return testSuites.indexOf( testSuite );
	}

	public String getBeforeRunScript()
	{
		return getConfig().isSetBeforeRunScript() ? getConfig().getBeforeRunScript().getStringValue() : null;
	}

	public void setBeforeRunScript( String script )
	{
		String oldScript = getBeforeRunScript();

		if( !getConfig().isSetBeforeRunScript() )
			getConfig().addNewBeforeRunScript();

		getConfig().getBeforeRunScript().setStringValue( script );
		if( beforeRunScriptEngine != null )
			beforeRunScriptEngine.setScript( script );

		notifyPropertyChanged( "beforeRunScript", oldScript, script );
	}

	public Object runBeforeRunScript( ProjectRunContext context, ProjectRunner runner ) throws Exception
	{
		String script = getBeforeRunScript();
		if( StringUtils.isNullOrEmpty( script ) )
			return null;

		if( beforeRunScriptEngine == null )
		{
			beforeRunScriptEngine = SoapUIScriptEngineRegistry.create( this );
			beforeRunScriptEngine.setScript( script );
		}

		beforeRunScriptEngine.setVariable( "runner", runner );
		beforeRunScriptEngine.setVariable( "context", context );
		beforeRunScriptEngine.setVariable( "project", this );
		beforeRunScriptEngine.setVariable( "log", SoapUI.ensureGroovyLog() );
		return beforeRunScriptEngine.run();
	}

	public String getAfterRunScript()
	{
		return getConfig().isSetAfterRunScript() ? getConfig().getAfterRunScript().getStringValue() : null;
	}

	public void setAfterRunScript( String script )
	{
		String oldScript = getAfterRunScript();

		if( !getConfig().isSetAfterRunScript() )
			getConfig().addNewAfterRunScript();

		getConfig().getAfterRunScript().setStringValue( script );
		if( afterRunScriptEngine != null )
			afterRunScriptEngine.setScript( script );

		notifyPropertyChanged( "afterRunScript", oldScript, script );
	}

	public Object runAfterRunScript( ProjectRunContext context, ProjectRunner runner ) throws Exception
	{
		String script = getAfterRunScript();
		if( StringUtils.isNullOrEmpty( script ) )
			return null;

		if( afterRunScriptEngine == null )
		{
			afterRunScriptEngine = SoapUIScriptEngineRegistry.create( this );
			afterRunScriptEngine.setScript( script );
		}

		afterRunScriptEngine.setVariable( "runner", runner );
		afterRunScriptEngine.setVariable( "context", context );
		afterRunScriptEngine.setVariable( "project", this );
		afterRunScriptEngine.setVariable( "log", SoapUI.ensureGroovyLog() );
		return afterRunScriptEngine.run();
	}

	public void addProjectRunListener( ProjectRunListener projectRunListener )
	{
		runListeners.add( projectRunListener );
	}

	public void removeProjectRunListener( ProjectRunListener projectRunListener )
	{
		runListeners.remove( projectRunListener );
	}

	public WsdlProjectRunner run( StringToObjectMap context, boolean async )
	{
		WsdlProjectRunner runner = new WsdlProjectRunner( this, context );
		runner.start( async );
		return runner;
	}

	public boolean isAbortOnError()
	{
		return getConfig().getAbortOnError();
	}

	// public boolean isFailOnErrors()
	// {
	// return getConfig().getFailOnErrors();
	// }
	//
	// public void setFailOnErrors( boolean arg0 )
	// {
	// getConfig().setFailOnErrors( arg0 );
	// }

	public void setAbortOnError( boolean arg0 )
	{
		getConfig().setAbortOnError( arg0 );
	}

	public long getTimeout()
	{
		return getConfig().getTimeout();
	}

	public void setTimeout( long timeout )
	{
		getConfig().setTimeout( timeout );
	}

	public ProjectRunListener[] getProjectRunListeners()
	{
		return runListeners.toArray( new ProjectRunListener[runListeners.size()] );
	}

	public TestSuiteRunType getRunType()
	{
		Enum runType = getConfig().getRunType();

		if( TestSuiteRunTypesConfig.PARALLELL.equals( runType ) )
			return TestSuiteRunType.PARALLEL;
		else
			return TestSuiteRunType.SEQUENTIAL;
	}

	public void setRunType( TestSuiteRunType runType )
	{
		TestSuiteRunType oldRunType = getRunType();

		if( runType == TestSuiteRunType.PARALLEL && oldRunType != TestSuiteRunType.PARALLEL )
		{
			getConfig().setRunType( TestSuiteRunTypesConfig.PARALLELL );
			notifyPropertyChanged( "runType", oldRunType, runType );
		}
		else if( runType == TestSuiteRunType.SEQUENTIAL && oldRunType != TestSuiteRunType.SEQUENTIAL )
		{
			getConfig().setRunType( TestSuiteRunTypesConfig.SEQUENTIAL );
			notifyPropertyChanged( "runType", oldRunType, runType );
		}
	}

	public WsdlTestSuite moveTestSuite( int ix, int offset )
	{
		WsdlTestSuite testSuite = testSuites.get( ix );

		if( offset == 0 )
			return testSuite;

		testSuites.remove( ix );
		testSuites.add( ix + offset, testSuite );

		TestSuiteConfig[] configs = new TestSuiteConfig[testSuites.size()];

		for( int c = 0; c < testSuites.size(); c++ )
		{
			if( offset > 0 )
			{
				if( c < ix )
					configs[c] = ( TestSuiteConfig )getConfig().getTestSuiteArray( c ).copy();
				else if( c < ( ix + offset ) )
					configs[c] = ( TestSuiteConfig )getConfig().getTestSuiteArray( c + 1 ).copy();
				else if( c == ix + offset )
					configs[c] = ( TestSuiteConfig )getConfig().getTestSuiteArray( ix ).copy();
				else
					configs[c] = ( TestSuiteConfig )getConfig().getTestSuiteArray( c ).copy();
			}
			else
			{
				if( c < ix + offset )
					configs[c] = ( TestSuiteConfig )getConfig().getTestSuiteArray( c ).copy();
				else if( c == ix + offset )
					configs[c] = ( TestSuiteConfig )getConfig().getTestSuiteArray( ix ).copy();
				else if( c <= ix )
					configs[c] = ( TestSuiteConfig )getConfig().getTestSuiteArray( c - 1 ).copy();
				else
					configs[c] = ( TestSuiteConfig )getConfig().getTestSuiteArray( c ).copy();
			}
		}

		getConfig().setTestSuiteArray( configs );
		for( int c = 0; c < configs.length; c++ )
		{
			testSuites.get( c ).resetConfigOnMove( getConfig().getTestSuiteArray( c ) );
		}

		fireTestSuiteMoved( testSuite, ix, offset );
		return testSuite;

	}

	public void importMockService( File file )
	{
		if( !file.exists() )
		{
			UISupport.showErrorMessage( "Error loading test case " );
			return;
		}

		MockServiceDocumentConfig newMockServiceConfig = null;

		try
		{
			newMockServiceConfig = MockServiceDocumentConfig.Factory.parse( file );
		}
		catch( Exception e )
		{
			SoapUI.logError( e );
		}

		if( newMockServiceConfig == null )
		{
			UISupport.showErrorMessage( "Not valid mock service xml" );
		}
		else
		{
			MockServiceConfig config = ( MockServiceConfig )projectDocument.getSoapuiProject().addNewMockService()
					.set( newMockServiceConfig.getMockService() );
			WsdlMockService mockService = new WsdlMockService( this, config );

			ModelSupport.unsetIds( mockService );
			mockService.afterLoad();

			mockServices.add( mockService );
			fireMockServiceAdded( mockService );

			resolveImportedMockService( mockService );
		}
	}

	private void resolveImportedMockService( WsdlMockService mockService )
	{
		ResolveDialog resolver = new ResolveDialog( "Validate MockService", "Checks MockService for inconsistencies",
				null );
		resolver.setShowOkMessage( false );
		resolver.resolve( mockService );
	}

	// we switch from check to scan, and then we work with scans only
	// quick and dirty way to get this done
	private static Map<String, String> checkToScanMap = initCheckToScanMap();

	private static Map<String, String> initCheckToScanMap()
	{
		Map<String, String> map = new HashMap<String, String>();
		map.put( "BoundaryCheck", "BoundaryScan" );
		map.put( "ParameterExposureCheck", "CrossSiteScriptingScan" );
		map.put( "FuzzingScan", "FuzzingScan" );
		map.put( "GroovySecurityCheck", "GroovySecurityScan" );
		map.put( "InvalidTypesSecurityCheck", "InvalidTypesSecurityScan" );
		map.put( "MalformedXmlSecurityCheck", "MalformedXmlSecurityScan" );
		map.put( "MaliciousAttachmentSecurityCheck", "MaliciousAttachmentSecurityScan" );
		map.put( "SQLInjectionCheck", "SQLInjectionScan" );
		map.put( "XmlBombSecurityCheck", "XmlBombSecurityScan" );
		map.put( "XPathInjectionSecurityCheck", "XPathInjectionSecurityScan" );
		return map;
	}

	public void addEnvironmentListener( EnvironmentListener listener )
	{
		environmentListeners.add( listener );
	}

	public void removeEnvironmentListener( EnvironmentListener listener )
	{
		environmentListeners.remove( listener );
	}

	//	protected void updateChecksToScans( ProjectConfig config )
	//	{
	//		for( TestSuiteConfig testSuite : config.getTestSuiteList() )
	//		{
	//			for( TestCaseConfig testCase : testSuite.getTestCaseList() )
	//			{
	//				for( SecurityTestConfig securityTest : testCase.getSecurityTestList() )
	//				{
	//					if( securityTest.getFailSecurityTestOnCheckErrors() )
	//					{
	//						securityTest.setFailSecurityTestOnScanErrors( securityTest.getFailSecurityTestOnCheckErrors() );
	//					}
	//
	//					for( TestStepSecurityTestConfig testStep : securityTest.getTestStepSecurityTestList() )
	//					{
	//						// get all checks
	//						List<SecurityCheckConfig> checkList = testStep.getTestStepSecurityCheckList();
	//
	//						// transform to scans
	//						for( SecurityCheckConfig check : checkList )
	//						{
	//							SecurityScanConfig scan = testStep.addNewTestStepSecurityScan();
	//
	//							scan.setName( check.getName() );
	//							scan.setTestStep( check.getTestStep() );
	//							scan.setExecutionStrategy( check.getExecutionStrategy() );
	//							scan.setType( checkToScanMap.get( check.getType() ) );
	//
	//							for( TestAssertionConfig assertion : check.getAssertionList() )
	//							{
	//								TestAssertionConfig assertion2 = scan.addNewAssertion();
	//
	//								assertion2.setConfiguration( assertion.getConfiguration() );
	//								assertion2.setType( assertion.getType() );
	//								assertion2.setName( assertion.getName() );
	//								if( assertion.isSetDescription() )
	//								{
	//									assertion2.setDescription( assertion.getDescription() );
	//								}
	//								if( assertion.isSetDisabled() )
	//								{
	//									assertion2.setDisabled( assertion.getDisabled() );
	//								}
	//								if( assertion.isSetId() )
	//								{
	//									assertion2.setId( assertion.getId() );
	//								}
	//								if( assertion.isSetTimestamp() )
	//								{
	//									assertion2.setTimestamp( assertion.getTimestamp() );
	//								}
	//							}
	//
	//							scan.setCheckedParameters( check.getCheckedPameters() );
	//
	//							XmlObject obj = check.getConfig();
	//
	//							if( obj instanceof InvalidSecurityCheckConfig )
	//							{
	//								InvalidSecurityScanConfig obj2 = InvalidSecurityScanConfig.Factory.newInstance();
	//								for( SchemaTypeForSecurityCheckConfig el : ( ( InvalidSecurityCheckConfig )obj )
	//										.getTypesListList() )
	//								{
	//									SchemaTypeForSecurityScanConfig type = obj2.addNewTypesList();
	//									type.setType( el.getType() );
	//									type.setValue( el.getValue() );
	//								}
	//							}
	//							else if( obj instanceof GroovySecurityCheckConfig )
	//							{
	//								GroovySecurityScanConfig obj2 = GroovySecurityScanConfig.Factory.newInstance();
	//								obj2.setExecuteScript( ( ( GroovySecurityCheckConfig )obj ).getExecuteScript() );
	//							}
	//							else if( obj instanceof ParameterExposureCheckConfig )
	//							{
	//								CrossSiteScriptingScanConfig obj2 = CrossSiteScriptingScanConfig.Factory.newInstance();
	//								for( String el : ( ( ParameterExposureCheckConfig )obj ).getParameterExposureStringsList() )
	//								{
	//									obj2.addParameterExposureStrings( el );
	//								}
	//							}
	//							else if( obj instanceof MaliciousAttachmentSecurityCheckConfig )
	//							{
	//								MaliciousAttachmentSecurityScanConfig obj2 = MaliciousAttachmentSecurityScanConfig.Factory
	//										.newInstance();
	//								obj2.setRequestTimeout( ( ( MaliciousAttachmentSecurityCheckConfig )obj ).getRequestTimeout() );
	//								for( MaliciousAttachmentElementConfig el : ( ( MaliciousAttachmentSecurityCheckConfig )obj )
	//										.getElementList() )
	//								{
	//									MaliciousAttachmentElementConfig el2 = obj2.addNewElement();
	//									el2.setKey( el.getKey() );
	//									el2.setRemove( el.getRemove() );
	//									for( MaliciousAttachmentConfig gen : el.getGenerateAttachmentList() )
	//									{
	//										MaliciousAttachmentConfig gen2 = el2.addNewGenerateAttachment();
	//										gen2.setCached( gen.getCached() );
	//										gen2.setContentType( gen.getContentType() );
	//										gen2.setEnabled( gen.getEnabled() );
	//										gen2.setFilename( gen.getFilename() );
	//										gen2.setSize( gen.getSize() );
	//									}
	//									for( MaliciousAttachmentConfig repl : el.getReplaceAttachmentList() )
	//									{
	//										MaliciousAttachmentConfig repl2 = el2.addNewReplaceAttachment();
	//										repl2.setCached( repl.getCached() );
	//										repl2.setContentType( repl.getContentType() );
	//										repl2.setEnabled( repl.getEnabled() );
	//										repl2.setFilename( repl.getFilename() );
	//										repl2.setSize( repl.getSize() );
	//									}
	//								}
	//							}
	//							else if( obj instanceof SQLInjectionCheckConfig )
	//							{
	//								SQLInjectionScanConfig obj2 = SQLInjectionScanConfig.Factory.newInstance();
	//								for( String el : ( ( SQLInjectionCheckConfig )obj ).getSqlInjectionStringsList() )
	//								{
	//									obj2.addSqlInjectionStrings( el );
	//								}
	//							}
	//							else if( obj instanceof XmlBombSecurityCheckConfig )
	//							{
	//								XmlBombSecurityScanConfig obj2 = XmlBombSecurityScanConfig.Factory.newInstance();
	//								obj2.setAttachXmlBomb( ( ( XmlBombSecurityCheckConfig )obj ).getAttachXmlBomb() );
	//								obj2.setXmlAttachmentPrefix( ( ( XmlBombSecurityCheckConfig )obj ).getXmlAttachmentPrefix() );
	//								for( String el : ( ( XmlBombSecurityCheckConfig )obj ).getXmlBombsList() )
	//								{
	//									obj2.addXmlBombs( el );
	//								}
	//							}
	//							else
	//							{
	//								// boundary scan, malformed xml, xpath
	//								scan.setConfig( obj );
	//							}
	//						}
	//
	//						// finally, remove checks
	//						testStep.setTestStepSecurityCheckArray( new SecurityCheckConfig[0] );
	//					}
	//				}
	//			}
	//		}
	//	}

}
