/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   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 .
 */

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", Boolean.valueOf( bCompressed ) );
        }
        catch( Exception e )
        {
            Error( "Can't set properties to substream '" + sStreamName + "', exception: " + e );
            return false;
        }

        // check size property of the stream
        try
        {
            long nSize = AnyConverter.toLong( 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
        {
            long nSize = AnyConverter.toLong( 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", Boolean.valueOf( 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", Boolean.valueOf( 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" ) );
                long nPropSize = AnyConverter.toLong( 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", 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 = " + ( Integer.valueOf( 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 committed 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 committed 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 );
    }
}

