/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 * 
 * Copyright 2000, 2010 Oracle and/or its affiliates.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/

// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_sc.hxx"



// INCLUDE ---------------------------------------------------------------

#include "scitems.hxx"
#include <sfx2/app.hxx>
#include <sfx2/docfile.hxx>
#include <sfx2/objsh.hxx>
#include <basic/sbmeth.hxx>
#include <basic/sbmod.hxx>
#include <basic/sbstar.hxx>
#include <basic/basmgr.hxx>

#include <basic/sbx.hxx>
#include <svl/zforlist.hxx>
#include <vcl/msgbox.hxx>
#include <tools/urlobj.hxx>
#include <rtl/math.hxx>

#include "validat.hxx"
#include "document.hxx"
#include "cell.hxx"
#include "patattr.hxx"
#include "rechead.hxx"
#include "globstr.hrc"
#include "rangenam.hxx"
#include "dbcolect.hxx"

#include <math.h>
#include <memory>

using namespace formula;
//------------------------------------------------------------------------

SV_IMPL_OP_PTRARR_SORT( ScValidationEntries_Impl, ScValidationDataPtr );

//------------------------------------------------------------------------

//
//	Eintrag fuer Gueltigkeit (es gibt nur eine Bedingung)
//

ScValidationData::ScValidationData( ScValidationMode eMode, ScConditionMode eOper,
							const String& rExpr1, const String& rExpr2,
							ScDocument* pDocument, const ScAddress& rPos,
                            const String& rExprNmsp1, const String& rExprNmsp2,
                            FormulaGrammar::Grammar eGrammar1, FormulaGrammar::Grammar eGrammar2 ) :
    ScConditionEntry( eOper, rExpr1, rExpr2, pDocument, rPos, rExprNmsp1, rExprNmsp2, eGrammar1, eGrammar2 ),
	nKey( 0 ),
    eDataMode( eMode ),
    eErrorStyle( SC_VALERR_STOP ),
    mnListType( ValidListType::UNSORTED )
{
    bShowInput = bShowError = sal_False;
}

ScValidationData::ScValidationData( ScValidationMode eMode, ScConditionMode eOper,
							const ScTokenArray* pArr1, const ScTokenArray* pArr2,
							ScDocument* pDocument, const ScAddress& rPos ) :
	ScConditionEntry( eOper, pArr1, pArr2, pDocument, rPos ),
	nKey( 0 ),
    eDataMode( eMode ),
    eErrorStyle( SC_VALERR_STOP ),
    mnListType( ValidListType::UNSORTED )
{
    bShowInput = bShowError = sal_False;
}

ScValidationData::ScValidationData( const ScValidationData& r ) :
	ScConditionEntry( r ),
	nKey( r.nKey ),
	eDataMode( r.eDataMode ),
	bShowInput( r.bShowInput ),
	bShowError( r.bShowError ),
	eErrorStyle( r.eErrorStyle ),
    mnListType( r.mnListType ),
	aInputTitle( r.aInputTitle ),
	aInputMessage( r.aInputMessage ),
	aErrorTitle( r.aErrorTitle ),
	aErrorMessage( r.aErrorMessage )
{
	//	Formeln per RefCount kopiert
}

ScValidationData::ScValidationData( ScDocument* pDocument, const ScValidationData& r ) :
	ScConditionEntry( pDocument, r ),
	nKey( r.nKey ),
	eDataMode( r.eDataMode ),
	bShowInput( r.bShowInput ),
	bShowError( r.bShowError ),
	eErrorStyle( r.eErrorStyle ),
    mnListType( r.mnListType ),
	aInputTitle( r.aInputTitle ),
	aInputMessage( r.aInputMessage ),
	aErrorTitle( r.aErrorTitle ),
	aErrorMessage( r.aErrorMessage )
{
	//	Formeln wirklich kopiert
}

ScValidationData::~ScValidationData()
{
}

sal_Bool ScValidationData::IsEmpty() const
{
	String aEmpty;
	ScValidationData aDefault( SC_VALID_ANY, SC_COND_EQUAL, aEmpty, aEmpty, GetDocument(), ScAddress() );
	return EqualEntries( aDefault );
}

sal_Bool ScValidationData::EqualEntries( const ScValidationData& r ) const
{
		//	gleiche Parameter eingestellt (ohne Key)

	return ScConditionEntry::operator==(r) &&
			eDataMode		== r.eDataMode &&
			bShowInput		== r.bShowInput &&
			bShowError		== r.bShowError &&
			eErrorStyle		== r.eErrorStyle &&
            mnListType      == r.mnListType &&
			aInputTitle		== r.aInputTitle &&
			aInputMessage	== r.aInputMessage &&
			aErrorTitle		== r.aErrorTitle &&
			aErrorMessage	== r.aErrorMessage;
}

void ScValidationData::ResetInput()
{
	bShowInput = sal_False;
}

void ScValidationData::ResetError()
{
	bShowError = sal_False;
}

void ScValidationData::SetInput( const String& rTitle, const String& rMsg )
{
	bShowInput = sal_True;
	aInputTitle = rTitle;
	aInputMessage = rMsg;
}

void ScValidationData::SetError( const String& rTitle, const String& rMsg,
									ScValidErrorStyle eStyle )
{
	bShowError = sal_True;
	eErrorStyle = eStyle;
	aErrorTitle = rTitle;
	aErrorMessage = rMsg;
}

sal_Bool ScValidationData::GetErrMsg( String& rTitle, String& rMsg,
									ScValidErrorStyle& rStyle ) const
{
	rTitle = aErrorTitle;
	rMsg   = aErrorMessage;
	rStyle = eErrorStyle;
	return bShowError;
}

sal_Bool ScValidationData::DoScript( const ScAddress& rPos, const String& rInput,
								ScFormulaCell* pCell, Window* pParent ) const
{
	ScDocument* pDocument = GetDocument();
	SfxObjectShell* pDocSh = pDocument->GetDocumentShell();
	if ( !pDocSh || !pDocument->CheckMacroWarn() )
		return sal_False;

	sal_Bool bScriptReturnedFalse = sal_False;	// Standard: kein Abbruch

	// Set up parameters
	::com::sun::star::uno::Sequence< ::com::sun::star::uno::Any > aParams(2);

	//	1) eingegebener / berechneter Wert
	String aValStr = rInput;
	double nValue;
	sal_Bool bIsValue = sal_False;
	if ( pCell )				// wenn Zelle gesetzt, aus Interpret gerufen
	{
		bIsValue = pCell->IsValue();
		if ( bIsValue )
			nValue  = pCell->GetValue();
		else
			pCell->GetString( aValStr );
	}
	if ( bIsValue )
		aParams[0] = ::com::sun::star::uno::makeAny( nValue );
	else
		aParams[0] = ::com::sun::star::uno::makeAny( ::rtl::OUString( aValStr ) );

	//	2) Position der Zelle
	String aPosStr;
	rPos.Format( aPosStr, SCA_VALID | SCA_TAB_3D, pDocument, pDocument->GetAddressConvention() );
	aParams[1] = ::com::sun::star::uno::makeAny( ::rtl::OUString( aPosStr ) );

	//	use link-update flag to prevent closing the document
	//	while the macro is running
	sal_Bool bWasInLinkUpdate = pDocument->IsInLinkUpdate();
	if ( !bWasInLinkUpdate )
		pDocument->SetInLinkUpdate( sal_True );

	if ( pCell )
		pDocument->LockTable( rPos.Tab() );

	::com::sun::star::uno::Any aRet;
	::com::sun::star::uno::Sequence< sal_Int16 > aOutArgsIndex;
	::com::sun::star::uno::Sequence< ::com::sun::star::uno::Any > aOutArgs;

	ErrCode eRet = pDocSh->CallXScript(
		aErrorTitle, aParams, aRet, aOutArgsIndex, aOutArgs );

	if ( pCell )
		pDocument->UnlockTable( rPos.Tab() );

	if ( !bWasInLinkUpdate )
		pDocument->SetInLinkUpdate( sal_False );

	// Check the return value from the script
	// The contents of the cell get reset if the script returns false
	sal_Bool bTmp = sal_False;
	if ( eRet == ERRCODE_NONE &&
			 aRet.getValueType() == getCppuBooleanType() &&
			 sal_True == ( aRet >>= bTmp ) &&
			 bTmp == sal_False )
	{
		bScriptReturnedFalse = sal_True;
	}

	if ( eRet == ERRCODE_BASIC_METHOD_NOT_FOUND && !pCell )
	// Makro nicht gefunden (nur bei Eingabe)
	{
		//!	andere Fehlermeldung, wenn gefunden, aber nicht bAllowed ??

		ErrorBox aBox( pParent, WinBits(WB_OK),
						ScGlobal::GetRscString( STR_VALID_MACRONOTFOUND ) );
		aBox.Execute();
	}

	return bScriptReturnedFalse;
}

	// sal_True -> Abbruch

sal_Bool ScValidationData::DoMacro( const ScAddress& rPos, const String& rInput,
								ScFormulaCell* pCell, Window* pParent ) const
{
	if ( SfxApplication::IsXScriptURL( aErrorTitle ) )
	{
		return DoScript( rPos, rInput, pCell, pParent );
	}

	ScDocument* pDocument = GetDocument();
	SfxObjectShell* pDocSh = pDocument->GetDocumentShell();
	if ( !pDocSh || !pDocument->CheckMacroWarn() )
		return sal_False;

	sal_Bool bDone = sal_False;
	sal_Bool bRet = sal_False;						// Standard: kein Abbruch

	//	Wenn das Dok waehrend eines Basic-Calls geladen wurde,
	//	ist das Sbx-Objekt evtl. nicht angelegt (?)
//	pDocSh->GetSbxObject();

	//	keine Sicherheitsabfrage mehr vorneweg (nur CheckMacroWarn), das passiert im CallBasic

#if 0
	// Makro-Name liegt in folgender Form vor:
	// "Macroname.Modulname.Libname.Dokumentname" oder
	// "Macroname.Modulname.Libname.Applikationsname"
	String aMacroName = aErrorTitle.GetToken(0, '.');
	String aModulName = aErrorTitle.GetToken(1, '.');
	String aLibName   = aErrorTitle.GetToken(2, '.');
	String aDocName   = aErrorTitle.GetToken(3, '.');
#endif

	//	Funktion ueber den einfachen Namen suchen,
	//	dann aBasicStr, aMacroStr fuer SfxObjectShell::CallBasic zusammenbauen

	StarBASIC* pRoot = pDocSh->GetBasic();
	SbxVariable* pVar = pRoot->Find( aErrorTitle, SbxCLASS_METHOD );
	if ( pVar && pVar->ISA(SbMethod) )
	{
		SbMethod* pMethod = (SbMethod*)pVar;
		SbModule* pModule = pMethod->GetModule();
		SbxObject* pObject = pModule->GetParent();
		String aMacroStr = pObject->GetName();
		aMacroStr += '.';
		aMacroStr += pModule->GetName();
		aMacroStr += '.';
		aMacroStr += pMethod->GetName();
		String aBasicStr;

		//	#95867# the distinction between document- and app-basic has to be done
		//	by checking the parent (as in ScInterpreter::ScMacro), not by looping
		//	over all open documents, because this may be called from within loading,
		//	when SfxObjectShell::GetFirst/GetNext won't find the document.

		if ( pObject->GetParent() )
			aBasicStr = pObject->GetParent()->GetName();	// Dokumentenbasic
		else
			aBasicStr = SFX_APP()->GetName();				// Applikationsbasic

		//	Parameter fuer Makro
		SbxArrayRef refPar = new SbxArray;

		//	1) eingegebener / berechneter Wert
		String aValStr = rInput;
        double nValue = 0.0;
		sal_Bool bIsValue = sal_False;
		if ( pCell )				// wenn Zelle gesetzt, aus Interpret gerufen
		{
			bIsValue = pCell->IsValue();
			if ( bIsValue )
				nValue  = pCell->GetValue();
			else
				pCell->GetString( aValStr );
		}
		if ( bIsValue )
			refPar->Get(1)->PutDouble( nValue );
		else
			refPar->Get(1)->PutString( aValStr );

		//	2) Position der Zelle
		String aPosStr;
		rPos.Format( aPosStr, SCA_VALID | SCA_TAB_3D, pDocument, pDocument->GetAddressConvention() );
		refPar->Get(2)->PutString( aPosStr );

		//	use link-update flag to prevent closing the document
		//	while the macro is running
		sal_Bool bWasInLinkUpdate = pDocument->IsInLinkUpdate();
		if ( !bWasInLinkUpdate )
			pDocument->SetInLinkUpdate( sal_True );

		if ( pCell )
			pDocument->LockTable( rPos.Tab() );
		SbxVariableRef refRes = new SbxVariable;
		ErrCode eRet = pDocSh->CallBasic( aMacroStr, aBasicStr, refPar, refRes );
		if ( pCell )
			pDocument->UnlockTable( rPos.Tab() );

		if ( !bWasInLinkUpdate )
			pDocument->SetInLinkUpdate( sal_False );

		//	Eingabe abbrechen, wenn Basic-Makro sal_False zurueckgibt
		if ( eRet == ERRCODE_NONE && refRes->GetType() == SbxBOOL && refRes->GetBool() == sal_False )
			bRet = sal_True;
		bDone = sal_True;
	}

	if ( !bDone && !pCell )			// Makro nicht gefunden (nur bei Eingabe)
	{
		//!	andere Fehlermeldung, wenn gefunden, aber nicht bAllowed ??

		ErrorBox aBox( pParent, WinBits(WB_OK),
						ScGlobal::GetRscString( STR_VALID_MACRONOTFOUND ) );
		aBox.Execute();
	}

	return bRet;
}

void ScValidationData::DoCalcError( ScFormulaCell* pCell ) const
{
	if ( eErrorStyle == SC_VALERR_MACRO )
		DoMacro( pCell->aPos, EMPTY_STRING, pCell, NULL );
}

	// sal_True -> Abbruch

sal_Bool ScValidationData::DoError( Window* pParent, const String& rInput,
								const ScAddress& rPos ) const
{
	if ( eErrorStyle == SC_VALERR_MACRO )
		return DoMacro( rPos, rInput, NULL, pParent );

	//	Fehlermeldung ausgeben

	String aTitle = aErrorTitle;
	if (!aTitle.Len())
		aTitle = ScGlobal::GetRscString( STR_MSSG_DOSUBTOTALS_0 );	// application title
	String aMessage = aErrorMessage;
	if (!aMessage.Len())
		aMessage = ScGlobal::GetRscString( STR_VALID_DEFERROR );

	//!	ErrorBox / WarningBox / InfoBox ?
	//!	(bei InfoBox immer nur OK-Button)

	WinBits nStyle = 0;
	switch (eErrorStyle)
	{
		case SC_VALERR_STOP:
			nStyle = WB_OK | WB_DEF_OK;
			break;
		case SC_VALERR_WARNING:
			nStyle = WB_OK_CANCEL | WB_DEF_CANCEL;
			break;
		case SC_VALERR_INFO:
			nStyle = WB_OK_CANCEL | WB_DEF_OK;
			break;
        default:
        {
            // added to avoid warnings
        }
	}

	MessBox aBox( pParent, WinBits(nStyle), aTitle, aMessage );
	sal_uInt16 nRet = aBox.Execute();

	return ( eErrorStyle == SC_VALERR_STOP || nRet == RET_CANCEL );
}


sal_Bool ScValidationData::IsDataValid( const String& rTest, const ScPatternAttr& rPattern,
									const ScAddress& rPos ) const
{
	if ( eDataMode == SC_VALID_ANY )
		return sal_True;						// alles erlaubt

	if ( rTest.GetChar(0) == '=' )
		return sal_False;						// Formeln sind sonst immer ungueltig

	if ( !rTest.Len() )
		return IsIgnoreBlank();				// leer: wie eingestellt

	SvNumberFormatter* pFormatter = GetDocument()->GetFormatTable();

	//	Test, was es denn ist - wie in ScColumn::SetString

	sal_uInt32 nFormat = rPattern.GetNumberFormat( pFormatter );

	double nVal;
	sal_Bool bIsVal = pFormatter->IsNumberFormat( rTest, nFormat, nVal );
	ScBaseCell* pCell;
	if (bIsVal)
		pCell = new ScValueCell( nVal );
	else
		pCell = new ScStringCell( rTest );

	sal_Bool bRet = IsDataValid( pCell, rPos );

	pCell->Delete();
	return bRet;
}

sal_Bool ScValidationData::IsDataValid( ScBaseCell* pCell, const ScAddress& rPos ) const
{
    if( eDataMode == SC_VALID_LIST )
        return IsListValid( pCell, rPos );

	double nVal = 0.0;
	String aString;
	sal_Bool bIsVal = sal_True;

	switch (pCell->GetCellType())
	{
		case CELLTYPE_VALUE:
			nVal = ((ScValueCell*)pCell)->GetValue();
			break;
		case CELLTYPE_STRING:
			((ScStringCell*)pCell)->GetString( aString );
			bIsVal = sal_False;
			break;
		case CELLTYPE_EDIT:
			((ScEditCell*)pCell)->GetString( aString );
			bIsVal = sal_False;
			break;
		case CELLTYPE_FORMULA:
			{
				ScFormulaCell* pFCell = (ScFormulaCell*)pCell;
				bIsVal = pFCell->IsValue();
				if ( bIsVal )
					nVal  = pFCell->GetValue();
				else
					pFCell->GetString( aString );
			}
			break;
		default:						// Notizen, Broadcaster
			return IsIgnoreBlank();		// wie eingestellt
	}

	sal_Bool bOk = sal_True;
	switch (eDataMode)
	{
		// SC_VALID_ANY schon oben

		case SC_VALID_WHOLE:
		case SC_VALID_DECIMAL:
		case SC_VALID_DATE:			// Date/Time ist nur Formatierung
		case SC_VALID_TIME:
			bOk = bIsVal;
			if ( bOk && eDataMode == SC_VALID_WHOLE )
				bOk = ::rtl::math::approxEqual( nVal, floor(nVal+0.5) );		// ganze Zahlen
			if ( bOk )
				bOk = IsCellValid( pCell, rPos );
			break;

		case SC_VALID_CUSTOM:
			//	fuer Custom muss eOp == SC_COND_DIRECT sein
			//!	der Wert muss im Dokument stehen !!!!!!!!!!!!!!!!!!!!
			bOk = IsCellValid( pCell, rPos );
			break;

		case SC_VALID_TEXTLEN:
			bOk = !bIsVal;			// nur Text
			if ( bOk )
			{
				double nLenVal = (double) aString.Len();
				ScValueCell aTmpCell( nLenVal );
				bOk = IsCellValid( &aTmpCell, rPos );
			}
			break;

		default:
			DBG_ERROR("hammanochnich");
			break;
	}

	return bOk;
}

// ----------------------------------------------------------------------------

namespace {

/** Token array helper. Iterates over all string tokens.
    @descr  The token array must contain separated string tokens only.
    @param bSkipEmpty  true = Ignores string tokens with empty strings. */
class ScStringTokenIterator
{
public:
    inline explicit             ScStringTokenIterator( ScTokenArray& rTokArr, bool bSkipEmpty = true ) :
                                    mrTokArr( rTokArr ), mbSkipEmpty( bSkipEmpty ), mbOk( true ) {}

    /** Returns the string of the first string token or NULL on error or empty token array. */
    const String*               First();
    /** Returns the string of the next string token or NULL on error or end of token array. */
    const String*               Next();

    /** Returns false, if a wrong token has been found. Does NOT return false on end of token array. */
    inline bool                 Ok() const { return mbOk; }

private:
    ScTokenArray&               mrTokArr;       /// The token array for iteration.
    bool                        mbSkipEmpty;    /// Ignore empty strings.
    bool                        mbOk;           /// true = correct token or end of token array.
};

const String* ScStringTokenIterator::First()
{
    mrTokArr.Reset();
    mbOk = true;
    return Next();
}

const String* ScStringTokenIterator::Next()
{
    if( !mbOk )
        return NULL;

    // seek to next non-separator token
    const FormulaToken* pToken = mrTokArr.NextNoSpaces();
    while( pToken && (pToken->GetOpCode() == ocSep) )
        pToken = mrTokArr.NextNoSpaces();

    mbOk = !pToken || (pToken->GetType() == formula::svString);
    const String* pString = (mbOk && pToken) ? &pToken->GetString() : NULL;
    // string found but empty -> get next token; otherwise return it
    return (mbSkipEmpty && pString && !pString->Len()) ? Next() : pString;
}

// ----------------------------------------------------------------------------

/** Returns the number format of the passed cell, or the standard format. */
sal_uLong lclGetCellFormat( ScDocument& rDoc, const ScAddress& rPos )
{
    const ScPatternAttr* pPattern = rDoc.GetPattern( rPos.Col(), rPos.Row(), rPos.Tab() );
    if( !pPattern )
        pPattern = rDoc.GetDefPattern();
    return pPattern->GetNumberFormat( rDoc.GetFormatTable() );
}

/** Inserts the passed string object. Always takes ownership. pData is invalid after this call! */
void lclInsertStringToCollection( TypedScStrCollection& rStrColl, TypedStrData* pData, bool bSorted )
{
    if( !(bSorted ? rStrColl.Insert( pData ) : rStrColl.AtInsert( rStrColl.GetCount(), pData )) )
        delete pData;
}

} // namespace

// ----------------------------------------------------------------------------

bool ScValidationData::HasSelectionList() const
{
    return (eDataMode == SC_VALID_LIST) && (mnListType != ValidListType::INVISIBLE);
}

bool ScValidationData::GetSelectionFromFormula( TypedScStrCollection* pStrings,
                                                ScBaseCell* pCell,
                                                const ScAddress& rPos,
                                                const ScTokenArray& rTokArr,
                                                int& rMatch ) const
{
    bool bOk = true;

    // pDoc is private in condition, use an accessor and a long winded name.
    ScDocument* pDocument = GetDocument();
    if( NULL == pDocument )
        return false;

    ScFormulaCell aValidationSrc( pDocument, rPos, &rTokArr,
           formula::FormulaGrammar::GRAM_DEFAULT, MM_FORMULA);

    // Make sure the formula gets interpreted and a result is delivered,
    // regardless of the AutoCalc setting.
    aValidationSrc.Interpret();

    ScMatrixRef xMatRef;
    const ScMatrix *pValues = aValidationSrc.GetMatrix();
    if (!pValues)
    {
        // The somewhat nasty case of either an error occured, or the
        // dereferenced value of a single cell reference or an immediate result
        // is stored as a single value.

        // Use an interim matrix to create the TypedStrData below.
        xMatRef = new ScMatrix(1,1);

        sal_uInt16 nErrCode = aValidationSrc.GetErrCode();
        if (nErrCode)
        {
            /* TODO : to use later in an alert box?
             * String rStrResult = "...";
             * rStrResult += ScGlobal::GetLongErrorString(nErrCode);
             */

            xMatRef->PutError( nErrCode, 0);
            bOk = false;
        }
        else if (aValidationSrc.HasValueData())
            xMatRef->PutDouble( aValidationSrc.GetValue(), 0);
        else
        {
            String aStr;
            aValidationSrc.GetString( aStr);
            xMatRef->PutString( aStr, 0);
        }

        pValues = xMatRef;
    }

    // which index matched.  We will want it eventually to pre-select that item.
    rMatch = -1;

    SvNumberFormatter* pFormatter = GetDocument()->GetFormatTable();

    bool    bSortList = (mnListType == ValidListType::SORTEDASCENDING);
    SCSIZE  nCol, nRow, nCols, nRows, n = 0;
    pValues->GetDimensions( nCols, nRows );

    sal_Bool bRef = sal_False;
    ScRange aRange;

    ScTokenArray* pArr = (ScTokenArray*) &rTokArr;
    pArr->Reset();
    ScToken* t = NULL;
    if (pArr->GetLen() == 1 && (t = static_cast<ScToken*>(pArr->GetNextReferenceOrName())) != NULL)
    {
        if (t->GetOpCode() == ocDBArea)
        {
            if( ScDBData* pDBData = pDocument->GetDBCollection()->FindIndex( t->GetIndex() ) )
            {
                pDBData->GetArea(aRange);
                bRef = sal_True;
            }
        }
        else if (t->GetOpCode() == ocName)
        {
            ScRangeData* pName = pDocument->GetRangeName()->FindIndex( t->GetIndex() );
            if (pName && pName->IsReference(aRange))
            {
                bRef = sal_True;
            }
        }
        else if (t->GetType() != svIndex)
        {
            t->CalcAbsIfRel(rPos);
            if (pArr->IsValidReference(aRange))
            {
                bRef = sal_True;
            }
        }
    }

    /* XL artificially limits things to a single col or row in the UI but does
     * not list the constraint in MOOXml. If a defined name or INDIRECT
     * resulting in 1D is entered in the UI and the definition later modified
     * to 2D, it is evaluated fine and also stored and loaded.  Lets get ahead
     * of the curve and support 2d. In XL, values are listed row-wise, do the
     * same. */
    for( nRow = 0; nRow < nRows ; nRow++ )
    {
        for( nCol = 0; nCol < nCols ; nCol++ )
        {
            ScTokenArray         aCondTokArr;
            TypedStrData*        pEntry = NULL;
            ScMatValType         nMatValType;
            String               aValStr;
            const ScMatrixValue* pMatVal = pValues->Get( nCol, nRow, nMatValType);

            // strings and empties
            if( NULL == pMatVal || ScMatrix::IsNonValueType( nMatValType ) )
            {
                if( NULL != pMatVal )
                    aValStr = pMatVal->GetString();

                if( NULL != pStrings )
                    pEntry = new TypedStrData( aValStr, 0.0, SC_STRTYPE_STANDARD);

                if( pCell && rMatch < 0 )
                    aCondTokArr.AddString( aValStr );
            }
            else
            {
                sal_uInt16 nErr = pMatVal->GetError();

                if( 0 != nErr )
                {
                    aValStr = ScGlobal::GetErrorString( nErr );
                }
                else
                {
                    // FIXME FIXME FIXME
                    // Feature regression.  Date formats are lost passing through the matrix
                    //pFormatter->GetInputLineString( pMatVal->fVal, 0, aValStr );
                    //For external reference and a formula that results in an area or array, date formats are still lost.
                    if ( bRef )
                    {
                        pDocument->GetInputString((SCCOL)(nCol+aRange.aStart.Col()), 
                            (SCROW)(nRow+aRange.aStart.Row()), aRange.aStart.Tab() , aValStr);
                    }
                    else
                        pFormatter->GetInputLineString( pMatVal->fVal, 0, aValStr );
                }

                if( pCell && rMatch < 0 )
                {
                    // I am not sure errors will work here, but a user can no
                    // manually enter an error yet so the point is somewhat moot.
                    aCondTokArr.AddDouble( pMatVal->fVal );
                }
                if( NULL != pStrings )
                    pEntry = new TypedStrData( aValStr, pMatVal->fVal, SC_STRTYPE_VALUE);
            }

            if( rMatch < 0 && NULL != pCell && IsEqualToTokenArray( pCell, rPos, aCondTokArr ) )
            {
                rMatch = n;
                // short circuit on the first match if not filling the list
                if( NULL == pStrings )
                    return true;
            }

            if( NULL != pEntry )
            {
                lclInsertStringToCollection( *pStrings, pEntry, bSortList );
                n++;
            }
        }
    }

    // In case of no match needed and an error occurred, return that error
    // entry as valid instead of silently failing.
    return bOk || NULL == pCell;
}

bool ScValidationData::FillSelectionList( TypedScStrCollection& rStrColl, const ScAddress& rPos ) const
{
    bool bOk = false;

    if( HasSelectionList() )
    {
        ::std::auto_ptr< ScTokenArray > pTokArr( CreateTokenArry( 0 ) );

        // *** try if formula is a string list ***

        bool bSortList = (mnListType == ValidListType::SORTEDASCENDING);
        sal_uInt32 nFormat = lclGetCellFormat( *GetDocument(), rPos );
        ScStringTokenIterator aIt( *pTokArr );
        for( const String* pString = aIt.First(); pString && aIt.Ok(); pString = aIt.Next() )
        {
            double fValue;
            bool bIsValue = GetDocument()->GetFormatTable()->IsNumberFormat( *pString, nFormat, fValue );
            TypedStrData* pData = new TypedStrData( *pString, fValue, bIsValue ? SC_STRTYPE_VALUE : SC_STRTYPE_STANDARD );
            lclInsertStringToCollection( rStrColl, pData, bSortList );
        }
        bOk = aIt.Ok();

        // *** if not a string list, try if formula results in a cell range or
        // anything else we recognize as valid ***

        if (!bOk)
        {
            int nMatch;
            bOk = GetSelectionFromFormula( &rStrColl, NULL, rPos, *pTokArr, nMatch );
        }
    }

    return bOk;
}

// ----------------------------------------------------------------------------

bool ScValidationData::IsEqualToTokenArray( ScBaseCell* pCell, const ScAddress& rPos, const ScTokenArray& rTokArr ) const
{
    // create a condition entry that tests on equality and set the passed token array
    ScConditionEntry aCondEntry( SC_COND_EQUAL, &rTokArr, NULL, GetDocument(), rPos );
    return aCondEntry.IsCellValid( pCell, rPos );
}

bool ScValidationData::IsListValid( ScBaseCell* pCell, const ScAddress& rPos ) const
{
    bool bIsValid = false;

    /*  Compare input cell with all supported tokens from the formula.
        Currently a formula may contain:
        1)  A list of strings (at least one string).
        2)  A single cell or range reference.
        3)  A single defined name (must contain a cell/range reference, another
            name, or DB range, or a formula resulting in a cell/range reference
            or matrix/array).
        4)  A single database range.
        5)  A formula resulting in a cell/range reference or matrix/array.
    */

    ::std::auto_ptr< ScTokenArray > pTokArr( CreateTokenArry( 0 ) );

    // *** try if formula is a string list ***

    sal_uInt32 nFormat = lclGetCellFormat( *GetDocument(), rPos );
    ScStringTokenIterator aIt( *pTokArr );
    for( const String* pString = aIt.First(); pString && aIt.Ok(); pString = aIt.Next() )
    {
        /*  Do not break the loop, if a valid string has been found.
            This is to find invalid tokens following in the formula. */
        if( !bIsValid )
        {
            // create a formula containing a single string or number
            ScTokenArray aCondTokArr;
            double fValue;
            if( GetDocument()->GetFormatTable()->IsNumberFormat( *pString, nFormat, fValue ) )
                aCondTokArr.AddDouble( fValue );
            else
                aCondTokArr.AddString( *pString );

            bIsValid = IsEqualToTokenArray( pCell, rPos, aCondTokArr );
        }
    }

    if( !aIt.Ok() )
        bIsValid = false;

    // *** if not a string list, try if formula results in a cell range or
    // anything else we recognize as valid ***

    if (!bIsValid)
    {
        int nMatch;
        bIsValid = GetSelectionFromFormula( NULL, pCell, rPos, *pTokArr, nMatch );
        bIsValid = bIsValid && nMatch >= 0;
    }

    return bIsValid;
}

// ============================================================================
// ============================================================================

ScValidationDataList::ScValidationDataList(const ScValidationDataList& rList) :
    ScValidationEntries_Impl()
{
	//	fuer Ref-Undo - echte Kopie mit neuen Tokens!

	sal_uInt16 nCount = rList.Count();

	for (sal_uInt16 i=0; i<nCount; i++)
		InsertNew( rList[i]->Clone() );

	//!		sortierte Eintraege aus rList schneller einfuegen ???
}

ScValidationDataList::ScValidationDataList(ScDocument* pNewDoc,
											const ScValidationDataList& rList)
{
	//	fuer neues Dokument - echte Kopie mit neuen Tokens!

	sal_uInt16 nCount = rList.Count();

	for (sal_uInt16 i=0; i<nCount; i++)
		InsertNew( rList[i]->Clone(pNewDoc) );

	//!		sortierte Eintraege aus rList schneller einfuegen ???
}

ScValidationData* ScValidationDataList::GetData( sal_uInt32 nKey )
{
	//!	binaer suchen

	sal_uInt16 nCount = Count();
	for (sal_uInt16 i=0; i<nCount; i++)
		if ((*this)[i]->GetKey() == nKey)
			return (*this)[i];

	DBG_ERROR("ScValidationDataList: Eintrag nicht gefunden");
	return NULL;
}

void ScValidationDataList::CompileXML()
{
	sal_uInt16 nCount = Count();
	for (sal_uInt16 i=0; i<nCount; i++)
		(*this)[i]->CompileXML();
}

void ScValidationDataList::UpdateReference( UpdateRefMode eUpdateRefMode,
								const ScRange& rRange, SCsCOL nDx, SCsROW nDy, SCsTAB nDz )
{
    sal_uInt16 nCount = Count();
    for (sal_uInt16 i=0; i<nCount; i++)
        (*this)[i]->UpdateReference( eUpdateRefMode, rRange, nDx, nDy, nDz);
}

void ScValidationDataList::UpdateMoveTab( SCTAB nOldPos, SCTAB nNewPos )
{
    sal_uInt16 nCount = Count();
    for (sal_uInt16 i=0; i<nCount; i++)
		(*this)[i]->UpdateMoveTab( nOldPos, nNewPos );
}

bool ScValidationDataList::MarkUsedExternalReferences() const
{
    bool bAllMarked = false;
	sal_uInt16 nCount = Count();
	for (sal_uInt16 i=0; !bAllMarked && i<nCount; i++)
		bAllMarked = (*this)[i]->MarkUsedExternalReferences();
    return bAllMarked;
}

sal_Bool ScValidationDataList::operator==( const ScValidationDataList& r ) const
{
	// fuer Ref-Undo - interne Variablen werden nicht verglichen

	sal_uInt16 nCount = Count();
	sal_Bool bEqual = ( nCount == r.Count() );
	for (sal_uInt16 i=0; i<nCount && bEqual; i++)			// Eintraege sind sortiert
		if ( !(*this)[i]->EqualEntries(*r[i]) )			// Eintraege unterschiedlich ?
			bEqual = sal_False;

	return bEqual;
}