/**************************************************************
 * 
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 * 
 *************************************************************/

package complex.storages;

import com.sun.star.uno.UnoRuntime;
import com.sun.star.uno.XInterface;
import com.sun.star.uno.AnyConverter;

import com.sun.star.lang.*;
import com.sun.star.embed.*;
import com.sun.star.packages.*;
import com.sun.star.io.*;
import com.sun.star.beans.*;

import share.LogWriter;

public class TestHelper  {

	LogWriter m_aLogWriter;
	String m_sTestPrefix;

	public TestHelper( LogWriter aLogWriter, String sTestPrefix )
	{
		m_aLogWriter = aLogWriter;
		m_sTestPrefix = sTestPrefix;
	}

	public boolean WriteBytesToStream( XStream xStream,
										String sStreamName,
										String sMediaType,
										boolean bCompressed,
										byte[] pBytes )
	{
		// get output stream of substream
		XOutputStream xOutput = xStream.getOutputStream();
		if ( xOutput == null )
		{
			Error( "Can't get XOutputStream implementation from substream '" + sStreamName + "'!" );
			return false;
		}
	
		// get XTrucate implementation from output stream
		XTruncate xTruncate = (XTruncate) UnoRuntime.queryInterface( XTruncate.class, xOutput );
		if ( xTruncate == null )
		{
			Error( "Can't get XTruncate implementation from substream '" + sStreamName + "'!" );
			return false;
		}
		
		// write requested byte sequence
		try
		{
			xTruncate.truncate();
			xOutput.writeBytes( pBytes );
		}
		catch( Exception e )
		{
			Error( "Can't write to stream '" + sStreamName + "', exception: " + e );
			return false;
		}

		// get access to the XPropertySet interface
		XPropertySet xPropSet = (XPropertySet) UnoRuntime.queryInterface( XPropertySet.class, xStream );
		if ( xPropSet == null )
		{
			Error( "Can't get XPropertySet implementation from substream '" + sStreamName + "'!" );
			return false;
		}

		// set properties to the stream
		try
		{
			xPropSet.setPropertyValue( "MediaType", sMediaType );
			xPropSet.setPropertyValue( "Compressed", new Boolean( bCompressed ) );
		}
		catch( Exception e )
		{
			Error( "Can't set properties to substream '" + sStreamName + "', exception: " + e );
			return false;
		}

		// check size property of the stream
		try
		{
			int nSize = AnyConverter.toInt( xPropSet.getPropertyValue( "Size" ) );
			if ( nSize != pBytes.length )
			{
				Error( "The 'Size' property of substream '" + sStreamName + "' contains wrong value!" );
				return false;
			}
		}
		catch( Exception e )
		{
			Error( "Can't get 'Size' property from substream '" + sStreamName + "', exception: " + e );
			return false;
		}

		return true;
	}

	public boolean WriteBytesToSubstreamDefaultCompressed( XStorage xStorage,
										                    String sStreamName,
										                    String sMediaType,
										                    byte[] pBytes )
	{
		// open substream element
		XStream xSubStream = null;
		try
		{
			Object oSubStream = xStorage.openStreamElement( sStreamName, ElementModes.WRITE );
			xSubStream = (XStream) UnoRuntime.queryInterface( XStream.class, oSubStream );
			if ( xSubStream == null )
			{
				Error( "Can't create substream '" + sStreamName + "'!" );
				return false;
			}
		}
		catch( Exception e )
		{
			Error( "Can't create substream '" + sStreamName + "', exception : " + e + "!" );
			return false;
		}
	
		// get output stream of substream
		XOutputStream xOutput = xSubStream.getOutputStream();
		if ( xOutput == null )
		{
			Error( "Can't get XOutputStream implementation from substream '" + sStreamName + "'!" );
			return false;
		}
	
		// get XTrucate implementation from output stream
		XTruncate xTruncate = (XTruncate) UnoRuntime.queryInterface( XTruncate.class, xOutput );
		if ( xTruncate == null )
		{
			Error( "Can't get XTruncate implementation from substream '" + sStreamName + "'!" );
			return false;
		}
		
		// write requested byte sequence
		try
		{
			xTruncate.truncate();
			xOutput.writeBytes( pBytes );
		}
		catch( Exception e )
		{
			Error( "Can't write to stream '" + sStreamName + "', exception: " + e );
			return false;
		}

		// get access to the XPropertySet interface
		XPropertySet xPropSet = (XPropertySet) UnoRuntime.queryInterface( XPropertySet.class, xSubStream );
		if ( xPropSet == null )
		{
			Error( "Can't get XPropertySet implementation from substream '" + sStreamName + "'!" );
			return false;
		}

		// set properties to the stream
        // do not set the compressed property
		try
		{
			xPropSet.setPropertyValue( "MediaType", sMediaType );
		}
		catch( Exception e )
		{
			Error( "Can't set properties to substream '" + sStreamName + "', exception: " + e );
			return false;
		}

		// check size property of the stream
		try
		{
			int nSize = AnyConverter.toInt( xPropSet.getPropertyValue( "Size" ) );
			if ( nSize != pBytes.length )
			{
				Error( "The 'Size' property of substream '" + sStreamName + "' contains wrong value!" );
				return false;
			}
		}
		catch( Exception e )
		{
			Error( "Can't get 'Size' property from substream '" + sStreamName + "', exception: " + e );
			return false;
		}

		// free the stream resources, garbage collector may remove the object too late
		if ( !disposeStream( xSubStream, sStreamName ) )
			return false;

		return true;
	}

	public boolean WriteBytesToSubstream( XStorage xStorage,
										  String sStreamName,
										  String sMediaType,
										  boolean bCompressed,
										  byte[] pBytes )
	{
		// open substream element
		XStream xSubStream = null;
		try
		{
			Object oSubStream = xStorage.openStreamElement( sStreamName, ElementModes.WRITE );
			xSubStream = (XStream) UnoRuntime.queryInterface( XStream.class, oSubStream );
			if ( xSubStream == null )
			{
				Error( "Can't create substream '" + sStreamName + "'!" );
				return false;
			}
		}
		catch( Exception e )
		{
			Error( "Can't create substream '" + sStreamName + "', exception : " + e + "!" );
			return false;
		}
		
		if ( !WriteBytesToStream( xSubStream, sStreamName, sMediaType, bCompressed, pBytes ) )
			return false;

		// free the stream resources, garbage collector may remove the object too late
		if ( !disposeStream( xSubStream, sStreamName ) )
			return false;

		return true;
	}

	public boolean WriteBytesToEncrSubstream( XStorage xStorage,
											  String sStreamName,
											  String sMediaType,
											  boolean bCompressed,
											  byte[] pBytes,
											  String sPass )
	{
		// open substream element
		XStream xSubStream = null;
		try
		{
			Object oSubStream = xStorage.openEncryptedStreamElement( sStreamName, ElementModes.WRITE, sPass );
			xSubStream = (XStream) UnoRuntime.queryInterface( XStream.class, oSubStream );
			if ( xSubStream == null )
			{
				Error( "Can't create substream '" + sStreamName + "'!" );
				return false;
			}
		}
		catch( Exception e )
		{
			Error( "Can't create substream '" + sStreamName + "', exception : " + e + "!" );
			return false;
		}

		if ( !WriteBytesToStream( xSubStream, sStreamName, sMediaType, bCompressed, pBytes ) )
			return false;

		// free the stream resources, garbage collector may remove the object too late
		if ( !disposeStream( xSubStream, sStreamName ) )
			return false;

		return true;
	}

	public boolean WBToSubstrOfEncr( XStorage xStorage,
											  String sStreamName,
											  String sMediaType,
											  boolean bCompressed,
											  byte[] pBytes,
											  boolean bEncrypted )
	{
		// open substream element
		XStream xSubStream = null;
		try
		{
			Object oSubStream = xStorage.openStreamElement( sStreamName, ElementModes.WRITE );
			xSubStream = (XStream) UnoRuntime.queryInterface( XStream.class, oSubStream );
			if ( xSubStream == null )
			{
				Error( "Can't create substream '" + sStreamName + "'!" );
				return false;
			}
		}
		catch( Exception e )
		{
			Error( "Can't create substream '" + sStreamName + "', exception : " + e + "!" );
			return false;
		}

		// get access to the XPropertySet interface
		XPropertySet xPropSet = (XPropertySet) UnoRuntime.queryInterface( XPropertySet.class, xSubStream );
		if ( xPropSet == null )
		{
			Error( "Can't get XPropertySet implementation from substream '" + sStreamName + "'!" );
			return false;
		}

		// set properties to the stream
		try
		{
			xPropSet.setPropertyValue( "UseCommonStoragePasswordEncryption", new Boolean( bEncrypted ) );
		}
		catch( Exception e )
		{
			Error( "Can't set 'UseCommonStoragePasswordEncryption' property to substream '" + sStreamName + "', exception: " + e );
			return false;
		}

		if ( !WriteBytesToStream( xSubStream, sStreamName, sMediaType, bCompressed, pBytes ) )
			return false;

		// free the stream resources, garbage collector may remove the object too late
		if ( !disposeStream( xSubStream, sStreamName ) )
			return false;

		return true;
	}

	public boolean WriteBytesToStreamH( XStorage xStorage,
										  String sStreamPath,
										  String sMediaType,
										  boolean bCompressed,
										  byte[] pBytes,
										  boolean bCommit )
	{
		// open substream element
		XStream xSubStream = null;
		try
		{
			XHierarchicalStorageAccess xHStorage =
				(XHierarchicalStorageAccess) UnoRuntime.queryInterface( XHierarchicalStorageAccess.class, xStorage );
			if ( xHStorage == null )
			{
				Error( "The storage does not support hierarchical access!" );
				return false;
			}

			Object oSubStream = xHStorage.openStreamElementByHierarchicalName( sStreamPath, ElementModes.WRITE );
			xSubStream = (XStream) UnoRuntime.queryInterface( XStream.class, oSubStream );
			if ( xSubStream == null )
			{
				Error( "Can't create substream '" + sStreamPath + "'!" );
				return false;
			}
		}
		catch( Exception e )
		{
			Error( "Can't create substream '" + sStreamPath + "', exception : " + e + "!" );
			return false;
		}
		
		if ( !WriteBytesToStream( xSubStream, sStreamPath, sMediaType, bCompressed, pBytes ) )
			return false;

		XTransactedObject xTransact =
			(XTransactedObject) UnoRuntime.queryInterface( XTransactedObject.class, xSubStream );
		if ( xTransact == null )
		{
			Error( "Substream '" + sStreamPath + "', stream opened for writing must be transacted!" );
			return false;
		}

		if ( bCommit )
		{
			try {
				xTransact.commit();
			} catch( Exception e )
			{
				Error( "Can't commit storage after substream '" + sStreamPath + "' change, exception : " + e + "!" );
				return false;
			}
		}

		// free the stream resources, garbage collector may remove the object too late
		if ( !disposeStream( xSubStream, sStreamPath ) )
			return false;

		return true;
	}

	public boolean WriteBytesToEncrStreamH( XStorage xStorage,
										  String sStreamPath,
										  String sMediaType,
										  boolean bCompressed,
										  byte[] pBytes,
										  String sPass,
										  boolean bCommit )
	{
		// open substream element
		XStream xSubStream = null;
		try
		{
			XHierarchicalStorageAccess xHStorage =
				(XHierarchicalStorageAccess) UnoRuntime.queryInterface( XHierarchicalStorageAccess.class, xStorage );
			if ( xHStorage == null )
			{
				Error( "The storage does not support hierarchical access!" );
				return false;
			}

			Object oSubStream = xHStorage.openEncryptedStreamElementByHierarchicalName( sStreamPath,
																						ElementModes.WRITE,
																						sPass );
			xSubStream = (XStream) UnoRuntime.queryInterface( XStream.class, oSubStream );
			if ( xSubStream == null )
			{
				Error( "Can't create substream '" + sStreamPath + "'!" );
				return false;
			}
		}
		catch( Exception e )
		{
			Error( "Can't create substream '" + sStreamPath + "', exception : " + e + "!" );
			return false;
		}
		
		if ( !WriteBytesToStream( xSubStream, sStreamPath, sMediaType, bCompressed, pBytes ) )
			return false;

		XTransactedObject xTransact =
			(XTransactedObject) UnoRuntime.queryInterface( XTransactedObject.class, xSubStream );
		if ( xTransact == null )
		{
			Error( "Substream '" + sStreamPath + "', stream opened for writing must be transacted!" );
			return false;
		}

		if ( bCommit )
		{
			try {
				xTransact.commit();
			} catch( Exception e )
			{
				Error( "Can't commit storage after substream '" + sStreamPath + "' change, exception : " + e + "!" );
				return false;
			}
		}

		// free the stream resources, garbage collector may remove the object too late
		if ( !disposeStream( xSubStream, sStreamPath ) )
			return false;

		return true;
	}

	public boolean WBToSubstrOfEncrH( XStorage xStorage,
									  String sStreamPath,
									  String sMediaType,
									  boolean bCompressed,
									  byte[] pBytes,
									  boolean bEncrypted,
									  boolean bCommit )
	{
		// open substream element
		XStream xSubStream = null;
		try
		{
			XHierarchicalStorageAccess xHStorage =
				(XHierarchicalStorageAccess) UnoRuntime.queryInterface( XHierarchicalStorageAccess.class, xStorage );
			if ( xHStorage == null )
			{
				Error( "The storage does not support hierarchical access!" );
				return false;
			}

			Object oSubStream = xHStorage.openStreamElementByHierarchicalName( sStreamPath, ElementModes.WRITE );
			xSubStream = (XStream) UnoRuntime.queryInterface( XStream.class, oSubStream );
			if ( xSubStream == null )
			{
				Error( "Can't create substream '" + sStreamPath + "'!" );
				return false;
			}
		}
		catch( Exception e )
		{
			Error( "Can't create substream '" + sStreamPath + "', exception : " + e + "!" );
			return false;
		}

		// get access to the XPropertySet interface
		XPropertySet xPropSet = (XPropertySet) UnoRuntime.queryInterface( XPropertySet.class, xSubStream );
		if ( xPropSet == null )
		{
			Error( "Can't get XPropertySet implementation from substream '" + sStreamPath + "'!" );
			return false;
		}

		// set properties to the stream
		try
		{
			xPropSet.setPropertyValue( "UseCommonStoragePasswordEncryption", new Boolean( bEncrypted ) );
		}
		catch( Exception e )
		{
			Error( "Can't set 'UseCommonStoragePasswordEncryption' property to substream '" + sStreamPath + "', exception: " + e );
			return false;
		}

		if ( !WriteBytesToStream( xSubStream, sStreamPath, sMediaType, bCompressed, pBytes ) )
			return false;

		XTransactedObject xTransact =
			(XTransactedObject) UnoRuntime.queryInterface( XTransactedObject.class, xSubStream );
		if ( xTransact == null )
		{
			Error( "Substream '" + sStreamPath + "', stream opened for writing must be transacted!" );
			return false;
		}

		if ( bCommit )
		{
			try {
				xTransact.commit();
			} catch( Exception e )
			{
				Error( "Can't commit storage after substream '" + sStreamPath + "' change, exception : " + e + "!" );
				return false;
			}
		}

		// free the stream resources, garbage collector may remove the object too late
		if ( !disposeStream( xSubStream, sStreamPath ) )
			return false;

		return true;
	}

	public int ChangeStreamPass( XStorage xStorage,
								 String sStreamName,
								 String sOldPass,
								 String sNewPass )
	{
		// open substream element
		XStream xSubStream = null;
		try
		{
			Object oSubStream = xStorage.openEncryptedStreamElement( sStreamName, ElementModes.WRITE, sOldPass );
			xSubStream = (XStream) UnoRuntime.queryInterface( XStream.class, oSubStream );
			if ( xSubStream == null )
			{
				Error( "Can't open substream '" + sStreamName + "'!" );
				return 0;
			}
		}
		catch( Exception e )
		{
			Error( "Can't open substream '" + sStreamName + "', exception : " + e + "!" );
			return 0;
		}


		// change the password for the stream
		XEncryptionProtectedSource xStreamEncryption =
				(XEncryptionProtectedSource) UnoRuntime.queryInterface( XEncryptionProtectedSource.class, xSubStream );

		if ( xStreamEncryption == null )
		{
			Message( "Optional interface XEncryptionProtectedSource is not implemented, feature can not be tested!" );
			return -1;
		}
			
		try {
			xStreamEncryption.setEncryptionPassword( sNewPass );
		}
		catch( Exception e )
		{
			Error( "Can't change encryption key of the substream '" + sStreamName + "', exception:" + e );
			return 0;
		}

		// free the stream resources, garbage collector may remove the object too late
		if ( !disposeStream( xSubStream, sStreamName ) )
			return 0;

		return 1;
	}

	public int ChangeStreamPassH( XStorage xStorage,
								 String sPath,
								 String sOldPass,
								 String sNewPass,
								 boolean bCommit )
	{
		// open substream element
		XHierarchicalStorageAccess xHStorage =
			(XHierarchicalStorageAccess) UnoRuntime.queryInterface( XHierarchicalStorageAccess.class, xStorage );
		if ( xHStorage == null )
		{
			Error( "The storage does not support hierarchical access!" );
			return 0;
		}

		XStream xSubStream = null;
		try
		{
			Object oSubStream = xHStorage.openEncryptedStreamElementByHierarchicalName( sPath, ElementModes.WRITE, sOldPass );
			xSubStream = (XStream) UnoRuntime.queryInterface( XStream.class, oSubStream );
			if ( xSubStream == null )
			{
				Error( "Can't open encrypted substream '" + sPath + "'!" );
				return 0;
			}
		}
		catch( Exception e )
		{
			Error( "Can't open encrypted substream '" + sPath + "', exception : " + e + "!" );
			return 0;
		}

		// change the password for the stream
		XEncryptionProtectedSource xStreamEncryption =
				(XEncryptionProtectedSource) UnoRuntime.queryInterface( XEncryptionProtectedSource.class, xSubStream );

		if ( xStreamEncryption == null )
		{
			Message( "Optional interface XEncryptionProtectedSource is not implemented, feature can not be tested!" );
			return -1;
		}
			
		try {
			xStreamEncryption.setEncryptionPassword( sNewPass );
		}
		catch( Exception e )
		{
			Error( "Can't change encryption key of the substream '" + sPath + "', exception:" + e );
			return 0;
		}

		XTransactedObject xTransact =
			(XTransactedObject) UnoRuntime.queryInterface( XTransactedObject.class, xSubStream );
		if ( xTransact == null )
		{
			Error( "Substream '" + sPath + "', stream opened for writing must be transacted!" );
			return 0;
		}

		if ( bCommit )
		{
			try {
				xTransact.commit();
			} catch( Exception e )
			{
				Error( "Can't commit storage after substream '" + sPath + "' change, exception : " + e + "!" );
				return 0;
			}
		}

		// free the stream resources, garbage collector may remove the object too late
		if ( !disposeStream( xSubStream, sPath ) )
			return 0;

		return 1;
	}

	public boolean setStorageTypeAndCheckProps( XStorage xStorage, String sMediaType, boolean bIsRoot, int nMode )
	{
		boolean bOk = false;

		// get access to the XPropertySet interface
		XPropertySet xPropSet = (XPropertySet) UnoRuntime.queryInterface( XPropertySet.class, xStorage );
		if ( xPropSet != null )
		{
			try
			{
				// set "MediaType" property to the stream
				xPropSet.setPropertyValue( "MediaType", sMediaType );

				// get "IsRoot" and "OpenMode" properties and control there values
				boolean bPropIsRoot = AnyConverter.toBoolean( xPropSet.getPropertyValue( "IsRoot" ) );
				int nPropMode = AnyConverter.toInt( xPropSet.getPropertyValue( "OpenMode" ) );

				bOk = true;
				if ( bPropIsRoot != bIsRoot )
				{
					Error( "'IsRoot' property contains wrong value!" );
					bOk = false;
				}
					
				if ( ( bIsRoot
				  && ( nPropMode | ElementModes.READ ) != ( nMode | ElementModes.READ ) )
				  || ( !bIsRoot && ( nPropMode & nMode ) != nMode ) )
				{
					Error( "'OpenMode' property contains wrong value, expected " + nMode + ", in reality " + nPropMode + "!" );
					bOk = false;
				}
			}
			catch( Exception e )
			{
				Error( "Can't control properties of substorage, exception: " + e );
			}
		}
		else
		{
			Error( "Can't get XPropertySet implementation from storage!" );
		}

		return bOk;
	}

	public boolean checkStorageProperties( XStorage xStorage, String sMediaType, boolean bIsRoot, int nMode )
	{
		boolean bOk = false;

		// get access to the XPropertySet interface
		XPropertySet xPropSet = (XPropertySet) UnoRuntime.queryInterface( XPropertySet.class, xStorage );
		if ( xPropSet != null )
		{
			try
			{
				// get "MediaType", "IsRoot" and "OpenMode" properties and control there values
				String sPropMediaType = AnyConverter.toString( xPropSet.getPropertyValue( "MediaType" ) );
				boolean bPropIsRoot = AnyConverter.toBoolean( xPropSet.getPropertyValue( "IsRoot" ) );
				int nPropMode = AnyConverter.toInt( xPropSet.getPropertyValue( "OpenMode" ) );

				bOk = true;
				if ( !sPropMediaType.equals( sMediaType ) )
				{
					Error( "'MediaType' property contains wrong value, expected '"
							+ sMediaType + "', set '" + sPropMediaType + "' !" );
					bOk = false;
				}
	
				if ( bPropIsRoot != bIsRoot )
				{
					Error( "'IsRoot' property contains wrong value!" );
					bOk = false;
				}
					
				if ( ( bIsRoot
				  && ( nPropMode | ElementModes.READ ) != ( nMode | ElementModes.READ ) )
				  || ( !bIsRoot && ( nPropMode & nMode ) != nMode ) )
				{
					Error( "'OpenMode' property contains wrong value, expected " + nMode + ", in reality " + nPropMode + "!" );
					bOk = false;
				}
			}
			catch( Exception e )
			{
				Error( "Can't get properties of substorage, exception: " + e );
			}
		}
		else
		{
			Error( "Can't get XPropertySet implementation from storage!" );
		}

		return bOk;
	}

	public boolean InternalCheckStream( XStream xStream,
										String sName,
										String sMediaType,
                                        boolean bCompressed,
										byte[] pBytes,
                                        boolean bCheckCompressed )
	{
		// get input stream of substream
		XInputStream xInput = xStream.getInputStream();
		if ( xInput == null )
		{
			Error( "Can't get XInputStream implementation from substream '" + sName + "'!" );
			return false;
		}
	
		byte pContents[][] = new byte[1][]; // ???

		// read contents
		try
		{
			xInput.readBytes( pContents, pBytes.length + 1 );
		}
		catch( Exception e )
		{
			Error( "Can't read from stream '" + sName + "', exception: " + e );
			return false;
		}

		// check size of stream data
		if ( pContents.length == 0 )
		{
			Error( "SubStream '" + sName + "' reading produced disaster!"  );
			return false;
		}

		if ( pBytes.length != pContents[0].length )
		{
			Error( "SubStream '" + sName + "' contains wrong amount of data! (" + pContents[0].length + "/" + pBytes.length + ")" );
			return false;
		}

		// check stream data
		for ( int ind = 0; ind < pBytes.length; ind++ )
		{
			if ( pBytes[ind] != pContents[0][ind] )
			{
				Error( "SubStream '" + sName + "' contains wrong data! ( byte num. "
						+ ind + " should be " + pBytes[ind] + " but it is " + pContents[0][ind] + ")" );
				return false;
			}
		}

		// check properties
		boolean bOk = false;

		// get access to the XPropertySet interface
		XPropertySet xPropSet = (XPropertySet) UnoRuntime.queryInterface( XPropertySet.class, xStream );
		if ( xPropSet != null )
		{
			try
			{
				// get "MediaType" and "Size" properties and control there values
				String sPropMediaType = AnyConverter.toString( xPropSet.getPropertyValue( "MediaType" ) );
				int nPropSize = AnyConverter.toInt( xPropSet.getPropertyValue( "Size" ) );
                boolean bPropCompress = AnyConverter.toBoolean( xPropSet.getPropertyValue( "Compressed" ) );

				bOk = true;
				if ( !sPropMediaType.equals( sMediaType ) )
				{
					Error( "'MediaType' property contains wrong value for stream '" + sName + "',\nexpected: '" 
							+ sMediaType + "', set: '" + sPropMediaType + "'!" );
					bOk = false;
				}
	
				if ( nPropSize != pBytes.length )
				{
					Error( "'Size' property contains wrong value for stream'" + sName + "'!" );
					bOk = false;
				}

                if ( bCheckCompressed && bPropCompress != bCompressed )
                {
    				Error( "'Compressed' property contains wrong value for stream'" + sName + "'!" );
					bOk = false;
                }
			}
			catch( Exception e )
			{
				Error( "Can't get properties of substream '" + sName + "', exception: " + e );
			}
		}
		else
		{
			Error( "Can't get XPropertySet implementation from stream '" + sName + "'!" );
		}

		return bOk;
	}

	public boolean checkStream( XStorage xParentStorage,
								String sName,
								String sMediaType,
                                boolean bCompressed,
								byte[] pBytes )
	{
		// open substream element first
		XStream xSubStream = null;
		try
		{
			Object oSubStream = xParentStorage.openStreamElement( sName, ElementModes.READ );
			xSubStream = (XStream) UnoRuntime.queryInterface( XStream.class, oSubStream );
			if ( xSubStream == null )
			{
				Error( "Can't open substream '" + sName + "'!" );
				return false;
			}
		}
		catch( Exception e )
		{
			Error( "Can't open substream '" + sName + "', exception : " + e + "!" );
			return false;
		}
		
		boolean bResult = InternalCheckStream( xSubStream, sName, sMediaType, bCompressed, pBytes, true );

		// free the stream resources, garbage collector may remove the object too late
		if ( !disposeStream( xSubStream, sName ) )
			return false;

		return bResult;
	}

	public boolean checkEncrStream( XStorage xParentStorage,
									String sName,
									String sMediaType,
									byte[] pBytes,
									String sPass )
	{
		// Important: a common password for any of parent storage should not be set or
		// 			  should be different from sPass

		try
		{
			Object oSubStream = xParentStorage.openStreamElement( sName, ElementModes.READ );
			Error( "Encrypted stream '" + sName + "' was opened without password!" );
			return false;
		}
		catch( WrongPasswordException wpe )
		{}
		catch( Exception e )
		{
			Error( "Unexpected exception in case of opening of encrypted stream '" + sName + "' without password: " + e + "!" );
			return false;
		}
	
		String sWrongPass = "11";
		sWrongPass += sPass;
		try
		{
			Object oSubStream = xParentStorage.openEncryptedStreamElement( sName, ElementModes.READ, sWrongPass );
			Error( "Encrypted stream '" + sName + "' was opened with wrong password!" );
			return false;
		}
		catch( WrongPasswordException wpe )
		{}
		catch( Exception e )
		{
			Error( "Unexpected exception in case of opening of encrypted stream '" + sName + "' with wrong password: " + e + "!" );
			return false;
		}
	
		XStream xSubStream = null;
		try
		{
			Object oSubStream = xParentStorage.openEncryptedStreamElement( sName, ElementModes.READ, sPass );
			xSubStream = (XStream) UnoRuntime.queryInterface( XStream.class, oSubStream );
			if ( xSubStream == null )
			{
				Error( "Can't open encrypted substream '" + sName + "'!" );
				return false;
			}
		}
		catch( Exception e )
		{
			Error( "Can't open encrypted substream '" + sName + "', exception : " + e + "!" );
			return false;
		}
		
        // encrypted streams will be compressed always, so after the storing this property is always true,
        // although before the storing it can be set to false ( it is not always clear whether a stream is encrypted
        // before the storing )
		boolean bResult = InternalCheckStream( xSubStream, sName, sMediaType, true, pBytes, false );

		// free the stream resources, garbage collector may remove the object too late
		if ( !disposeStream( xSubStream, sName ) )
			return false;

		return bResult;
	}

	public boolean checkStreamH( XStorage xParentStorage,
								String sPath,
								String sMediaType,
                                boolean bCompressed,
								byte[] pBytes )
	{
		// open substream element first
		XStream xSubStream = null;
		try
		{
			XHierarchicalStorageAccess xHStorage =
				(XHierarchicalStorageAccess) UnoRuntime.queryInterface( XHierarchicalStorageAccess.class, xParentStorage );
			if ( xHStorage == null )
			{
				Error( "The storage does not support hierarchical access!" );
				return false;
			}

			Object oSubStream = xHStorage.openStreamElementByHierarchicalName( sPath, ElementModes.READ );
			xSubStream = (XStream) UnoRuntime.queryInterface( XStream.class, oSubStream );
			if ( xSubStream == null )
			{
				Error( "Can't open substream '" + sPath + "'!" );
				return false;
			}
		}
		catch( Exception e )
		{
			Error( "Can't open substream '" + sPath + "', exception : " + e + "!" );
			return false;
		}
		
		boolean bResult = InternalCheckStream( xSubStream, sPath, sMediaType, bCompressed, pBytes, true );

		// free the stream resources, garbage collector may remove the object too late
		if ( !disposeStream( xSubStream, sPath ) )
			return false;

		return bResult;
	}

	public boolean checkEncrStreamH( XStorage xParentStorage,
									String sPath,
									String sMediaType,
									byte[] pBytes,
									String sPass )
	{
		// Important: a common password for any of parent storage should not be set or
		// 			  should be different from sPass
		XHierarchicalStorageAccess xHStorage =
			(XHierarchicalStorageAccess) UnoRuntime.queryInterface( XHierarchicalStorageAccess.class, xParentStorage );
		if ( xHStorage == null )
		{
			Error( "The storage does not support hierarchical access!" );
			return false;
		}

		try
		{
			Object oSubStream = xHStorage.openStreamElementByHierarchicalName( sPath, ElementModes.READ );
			XStream xSubStream = (XStream) UnoRuntime.queryInterface( XStream.class, oSubStream );
			Error( "Encrypted substream '" + sPath + "' was opened without password!" );
			return false;
		}
		catch( WrongPasswordException wpe )
		{}
		catch( Exception e )
		{
			Error( "Unexpected exception in case of opening of encrypted stream '" + sPath + "' without password: " + e + "!" );
			return false;
		}
	
		String sWrongPass = "11";
		sWrongPass += sPass;
		try
		{
			Object oSubStream = xHStorage.openEncryptedStreamElementByHierarchicalName( sPath, ElementModes.READ, sWrongPass );
			XStream xSubStream = (XStream) UnoRuntime.queryInterface( XStream.class, oSubStream );
			Error( "Encrypted substream '" + sPath + "' was opened with wrong password!" );
			return false;
		}
		catch( WrongPasswordException wpe )
		{}
		catch( Exception e )
		{
			Error( "Unexpected exception in case of opening of encrypted stream '" + sPath + "' with wrong password: " + e + "!" );
			return false;
		}
	
		XStream xSubStream = null;
		try
		{
			Object oSubStream = xHStorage.openEncryptedStreamElementByHierarchicalName( sPath, ElementModes.READ, sPass );
			xSubStream = (XStream) UnoRuntime.queryInterface( XStream.class, oSubStream );
			if ( xSubStream == null )
			{
				Error( "Can't open encrypted substream '" + sPath + "'!" );
				return false;
			}
		}
		catch( Exception e )
		{
			Error( "Can't open encrypted substream '" + sPath + "', exception : " + e + "!" );
			return false;
		}
		
        // encrypted streams will be compressed always, so after the storing this property is always true,
        // although before the storing it can be set to false ( it is not always clear whether a stream is encrypted
        // before the storing )
		boolean bResult = InternalCheckStream( xSubStream, sPath, sMediaType, true, pBytes, false );

		// free the stream resources, garbage collector may remove the object too late
		if ( !disposeStream( xSubStream, sPath ) )
			return false;

		return bResult;
	}

	public boolean copyStorage( XStorage xSourceStorage, XStorage xDestStorage )
	{
		// copy xSourceStorage to xDestStorage
		try
		{
			xSourceStorage.copyToStorage( xDestStorage );
		}
		catch( Exception e )
		{
			Error( "Storage copying failed, exception: " + e );
			return false;
		}

		return true;
	}

	public boolean commitStorage( XStorage xStorage )
	{
		// XTransactedObject must be supported by storages
		XTransactedObject xTransact = (XTransactedObject) UnoRuntime.queryInterface( XTransactedObject.class, xStorage );
		if ( xTransact == null )
		{
			Error( "Storage doesn't implement transacted access!" );
			return false;
		}

		try
		{
			xTransact.commit();
		}
		catch( Exception e )
		{
			Error( "Storage commit failed, exception:" + e );
			return false;
		}

		return true;
	}
	
	public boolean disposeStream( XStream xStream, String sStreamName )
	{
		XComponent xComponent = (XComponent) UnoRuntime.queryInterface( XComponent.class, xStream );
		if ( xComponent == null )
		{
			Error( "Can't get XComponent implementation from substream '" + sStreamName + "'!" );
			return false;
		}
		
		try
		{
			xComponent.dispose();
		}
		catch( Exception e )
		{
			Error( "Substream '" + sStreamName + "' disposing throws exception: " + e );
			return false;
		}

		return true;
	}

	public boolean disposeStorage( XStorage xStorage )
	{
		// dispose the storage
		XComponent xComponent = (XComponent) UnoRuntime.queryInterface( XComponent.class, xStorage );
		if ( xComponent == null )
		{
			Error( "Can't retrieve XComponent implementation from storage!" );
			return false;
		}

		try
		{
			xComponent.dispose();
		}
		catch( Exception e )
		{
			Error( "Storage disposing failed!" );
			return false;
		}

		return true;
	}

	public XInputStream getInputStream( XStream xStream )
	{
		XInputStream xInTemp = null;
		try
		{
			xInTemp = xStream.getInputStream();
			if ( xInTemp == null )
				Error( "Can't get the input part of a stream!" );
		}
		catch ( Exception e )
		{
			Error( "Can't get the input part of a stream, exception :" + e );
		}

		return xInTemp;
	}

	public boolean closeOutput( XStream xStream )
	{
		XOutputStream xOutTemp = null;
		try
		{
			xOutTemp = xStream.getOutputStream();
			if ( xOutTemp == null )
			{
				Error( "Can't get the output part of a stream!" );
				return false;
			}
		}
		catch ( Exception e )
		{
			Error( "Can't get the output part of a stream, exception :" + e );
			return false;
		}

		try
		{
			xOutTemp.closeOutput();
		}
		catch ( Exception e )
		{
			Error( "Can't close output part of a stream, exception :" + e );
			return false;
		}
		
		return true;
	}

	public XStorage openSubStorage( XStorage xStorage, String sName, int nMode )
	{
		// open existing substorage
		try
		{
			Object oSubStorage = xStorage.openStorageElement( sName, nMode );
			XStorage xSubStorage = (XStorage) UnoRuntime.queryInterface( XStorage.class, oSubStorage );
			return xSubStorage;
		}
		catch( Exception e )
		{
			Error( "Can't open substorage '" + sName + "', exception: " + e );
		}

		return null;
	}

	public XStream CreateTempFileStream( XMultiServiceFactory xMSF )
	{
		// try to get temporary file representation
		XStream xTempFileStream = null;
		try
		{
			Object oTempFile = xMSF.createInstance( "com.sun.star.io.TempFile" );
			xTempFileStream = (XStream)UnoRuntime.queryInterface( XStream.class, oTempFile );
		}
		catch( Exception e )
		{}

		if ( xTempFileStream == null )
			Error( "Can't create temporary file!" );

		return xTempFileStream;
	}

	public String CreateTempFile( XMultiServiceFactory xMSF )
	{
		String sResult = null;
	
		// try to get temporary file representation
		XPropertySet xTempFileProps = null;
		try
		{
			Object oTempFile = xMSF.createInstance( "com.sun.star.io.TempFile" );
			xTempFileProps = (XPropertySet)UnoRuntime.queryInterface( XPropertySet.class, oTempFile );
		}
		catch( Exception e )
		{}

		if ( xTempFileProps != null )
		{
			try
			{
				xTempFileProps.setPropertyValue( "RemoveFile", new Boolean( false ) );
				sResult = AnyConverter.toString( xTempFileProps.getPropertyValue( "Uri" ) );
			}
			catch( Exception e )
			{
				Error( "Can't control TempFile properties, exception: " + e );
			}
		}
		else
		{
			Error( "Can't create temporary file representation!" );
		}

		// close temporary file explicitly
		try
		{
			XStream xStream = (XStream)UnoRuntime.queryInterface( XStream.class, xTempFileProps );
			if ( xStream != null )
			{
				XOutputStream xOut = xStream.getOutputStream();
				if ( xOut != null )
					xOut.closeOutput();

				XInputStream xIn = xStream.getInputStream();
				if ( xIn != null )
					xIn.closeInput();
			}
			else
				Error( "Can't close TempFile!" );
		}
		catch( Exception e )
		{
			Error( "Can't close TempFile, exception: " + e );
		}

		return sResult;
	}

	public boolean copyElementTo( XStorage xSource, String sName, XStorage xDest )
	{
		// copy element with name sName from xSource to xDest
		try
		{
			xSource.copyElementTo( sName, xDest, sName );
		}
		catch( Exception e )
		{
			Error( "Element copying failed, exception: " + e );
			return false;
		}

		return true;
	}

	public boolean copyElementTo( XStorage xSource, String sName, XStorage xDest, String sTargetName )
	{
		// copy element with name sName from xSource to xDest
		try
		{
			xSource.copyElementTo( sName, xDest, sTargetName );
		}
		catch( Exception e )
		{
			Error( "Element copying failed, exception: " + e );
			return false;
		}

		return true;
	}

	public boolean moveElementTo( XStorage xSource, String sName, XStorage xDest )
	{
		// move element with name sName from xSource to xDest
		try
		{
			xSource.moveElementTo( sName, xDest, sName );
		}
		catch( Exception e )
		{
			Error( "Element moving failed, exception: " + e );
			return false;
		}

		return true;
	}

	public boolean renameElement( XStorage xStorage, String sOldName, String sNewName )
	{
		// rename element with name sOldName to sNewName
		try
		{
			xStorage.renameElement( sOldName, sNewName );
		}
		catch( Exception e )
		{
			Error( "Element renaming failed, exception: " + e );
			return false;
		}

		return true;
	}

	public boolean removeElement( XStorage xStorage, String sName )
	{
		// remove element with name sName
		try
		{
			xStorage.removeElement( sName );
		}
		catch( Exception e )
		{
			Error( "Element removing failed, exception: " + e );
			return false;
		}

		return true;
	}

	public XStream OpenStream( XStorage xStorage,
								String sStreamName,
								int nMode )
	{
		// open substream element
		XStream xSubStream = null;
		try
		{
			Object oSubStream = xStorage.openStreamElement( sStreamName, nMode );
			xSubStream = (XStream) UnoRuntime.queryInterface( XStream.class, oSubStream );
			if ( xSubStream == null )
				Error( "Can't create substream '" + sStreamName + "'!" );
		}
		catch( Exception e )
		{
			Error( "Can't create substream '" + sStreamName + "', exception : " + e + "!" );
		}

		return xSubStream;
	}

	public boolean compareRawMethodsOnEncrStream( XStorage xStorage, String sStreamName )
	{

		XStorageRawAccess xRawStorage;
		try
		{
			xRawStorage = (XStorageRawAccess) UnoRuntime.queryInterface( XStorageRawAccess.class, xStorage );
		}
		catch( Exception e )
		{
			Error( "Can't get raw access to the storage, exception : " + e + "!" );
			return false;
		}

		if ( xRawStorage == null )
		{
			Error( "Can't get raw access to the storage!" );
			return false;
		}

		XInputStream xHeadRawStream = null;
		try
		{
			xHeadRawStream = xRawStorage.getRawEncrStreamElement( sStreamName );
		}
		catch( Exception e )
		{
			Error( "Can't open encrypted stream '" + sStreamName + "' in raw mode with header, exception : " + e + "!" );
		}

		XInputStream xPlainRawStream = null;
		try
		{
			xPlainRawStream = xRawStorage.getPlainRawStreamElement( sStreamName );
		}
		catch( Exception e )
		{
			Error( "Can't open encrypted stream '" + sStreamName + "' in raw mode with header, exception : " + e + "!" );
		}

		if ( xHeadRawStream == null || xPlainRawStream == null )
		{
			Error( "Can't open encrypted stream '" + sStreamName + "' in raw modes!" );
			return false;
		}

		try
		{
			byte pData[][] = new byte[1][38];
			if ( xHeadRawStream.readBytes( pData, 38 ) != 38 )
			{
				Error( "Can't read header of encrypted stream '" + sStreamName + "' raw representations!" );
				return false;
			}

			if ( pData[0][0] != 0x4d || pData[0][1] != 0x4d || pData[0][2] != 0x02 || pData[0][3] != 0x05 )
			{
				Error( "No signature in the header of encrypted stream '" + sStreamName + "' raw representations!" );
				return false;
			}

			int nVariableHeaderLength =
						( pData[0][30] + pData[0][31] * 0x100 ) // salt length
						+ ( pData[0][32] + pData[0][33] * 0x100 ) // iv length
						+ ( pData[0][34] + pData[0][35] * 0x100 ) // digest length
						+ ( pData[0][36] + pData[0][37] * 0x100 ); // mediatype length

			xHeadRawStream.skipBytes( nVariableHeaderLength );

			byte pRawData1[][] = new byte[1][32000];
			byte pRawData2[][] = new byte[1][32000];
			int nRead1 = 0;
			int nRead2 = 0;

			do
			{
				nRead1 = xHeadRawStream.readBytes( pRawData1, 32000 );
				nRead2 = xPlainRawStream.readBytes( pRawData2, 32000 );

				if ( nRead1 != nRead2 )
				{
					Error( "The encrypted stream '" + sStreamName + "' raw representations have different size! nRead1 - nRead2 = " + ( new Integer( nRead1 - nRead2 ) ).toString() );
					return false;
				}

				for ( int nInd = 0; nInd < nRead1; nInd++ )
					if ( pRawData1[0][nInd] != pRawData2[0][nInd] )
					{
						Error( "The encrypted stream '" + sStreamName + "' raw representations have different data!" );
						return false;
					}
			}
			while( nRead1 == 32000 );
		}
		catch ( Exception e )
		{
			Error( "Can't compare stream '" + sStreamName + "' raw representations, exception : " + e + "!" );
			return false;
		}

		return true;
	}

	public boolean cantOpenStorage( XStorage xStorage, String sName )
	{
		// try to open an opened substorage, open call must fail
		try
		{
			Object oDummyStorage = xStorage.openStorageElement( sName, ElementModes.READ );
			Error( "The trying to reopen opened substorage '" + sName + "' must fail!" );
		}
		catch( Exception e )
		{
			return true;
		}

		return false;
	}

	public boolean cantOpenStream( XStorage xStorage, String sName, int nMode )
	{
		// try to open the substream with specified mode must fail
		try
		{
			Object oDummyStream = xStorage.openStreamElement( sName, nMode );
			Error( "The trying to open substream '" + sName + "' must fail!" );
		}
		catch( Exception e )
		{
			return true;
		}

		return false;
	}

	public boolean cantOpenStreamH( XStorage xStorage, String sPath, int nMode )
	{
		// try to open the substream with specified mode must fail

		XHierarchicalStorageAccess xHStorage =
			(XHierarchicalStorageAccess) UnoRuntime.queryInterface( XHierarchicalStorageAccess.class, xStorage );
		if ( xHStorage == null )
		{
			Error( "The storage does not support hierarchical access!" );
			return false;
		}

		try
		{
			Object oDummyStream = xHStorage.openStreamElementByHierarchicalName( sPath, nMode );
			Error( "The trying to open substream '" + sPath + "' must fail!" );
		}
		catch( Exception e )
		{
			return true;
		}

		return false;
	}

	public boolean cantOpenEncrStreamH( XStorage xStorage, String sPath, int nMode, String aPass )
	{
		// try to open the substream with specified mode must fail

		XHierarchicalStorageAccess xHStorage =
			(XHierarchicalStorageAccess) UnoRuntime.queryInterface( XHierarchicalStorageAccess.class, xStorage );
		if ( xHStorage == null )
		{
			Error( "The storage does not support hierarchical access!" );
			return false;
		}

		try
		{
			Object oDummyStream = xHStorage.openEncryptedStreamElementByHierarchicalName( sPath, nMode, aPass );
			Error( "The trying to open substream '" + sPath + "' must fail!" );
		}
		catch( WrongPasswordException wpe )
		{
			Error( "The substream '" + sPath + "' must not exist!" );
			return false;
		}
		catch( Exception e )
		{
			return true;
		}

		return false;
	}

	public XStorage cloneStorage( XSingleServiceFactory xFactory, XStorage xStorage )
	{
		// create a copy of a last commited version of specified storage
		XStorage xResult = null;
		try
		{
			Object oTempStorage = xFactory.createInstance();
			xResult = (XStorage) UnoRuntime.queryInterface( XStorage.class, oTempStorage );
			if ( xResult != null )
				xStorage.copyLastCommitTo( xResult );
		}
		catch( Exception e )
		{
			Error( "Can't clone storage, exception: " + e );
			return null;
		}

		return xResult;
	}

	public XStorage cloneSubStorage( XSingleServiceFactory xFactory, XStorage xStorage, String sName )
	{
		// create a copy of a last commited version of specified substorage
		XStorage xResult = null;
		try
		{
			Object oTempStorage = xFactory.createInstance();
			xResult = (XStorage) UnoRuntime.queryInterface( XStorage.class, oTempStorage );
			if ( xResult != null )
				xStorage.copyStorageElementLastCommitTo( sName, xResult );
		}
		catch( Exception e )
		{
			Error( "Can't clone substorage '" + sName + "', exception: " + e );
			return null;
		}

		return xResult;
	}

	public XStream cloneSubStream( XStorage xStorage, String sName )
	{
		// clone existing substream
		try
		{
			XStream xStream = xStorage.cloneStreamElement( sName );
			return xStream;
		}
		catch( Exception e )
		{
			Error( "Can't clone substream '" + sName + "', exception: " + e );
		}

		return null;
	}

	public XStream cloneEncrSubStream( XStorage xStorage, String sName, String sPass )
	{
		// clone existing substream
		try
		{
			XStream xStream = xStorage.cloneEncryptedStreamElement( sName, sPass );
			return xStream;
		}
		catch( Exception e )
		{
			Error( "Can't clone encrypted substream '" + sName + "', exception: " + e );
		}

		return null;
	}

	public void Error( String sError )
	{
		m_aLogWriter.println( m_sTestPrefix + "Error: " + sError );
	}

	public void Message( String sMessage )
	{
		m_aLogWriter.println( m_sTestPrefix + sMessage );
	}
}

