<commit_before>/*************************************************************************
 *
 *  $RCSfile: TableWindowListBox.cxx,v $
 *
 *  $Revision: 1.24 $
 *
 *  last change: $Author: oj $ $Date: 2002-05-06 09:51:37 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 by Sun Microsystems, Inc.
 *  901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License version 2.1, as published by the Free Software Foundation.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *  MA  02111-1307  USA
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (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.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an AS IS basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 *
 *  Copyright: 2000 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): _______________________________________
 *
 *
 ************************************************************************/
#ifndef DBAUI_TABLEWINDOWLISTBOX_HXX
#include "TableWindowListBox.hxx"
#endif
#ifndef DBAUI_TABLEWINDOW_HXX
#include "TableWindow.hxx"
#endif
#ifndef DBAUI_QUERYDESIGNVIEW_HXX
#include "QueryDesignView.hxx"
#endif
#ifndef DBAUI_QUERYTABLEVIEW_HXX
#include "QueryTableView.hxx"
#endif
#ifndef DBAUI_QUERYCONTROLLER_HXX
#include "querycontroller.hxx"
#endif
#ifndef DBAUI_JOINEXCHANGE_HXX
#include "JoinExchange.hxx"
#endif
#ifndef _TOOLS_DEBUG_HXX
#include <tools/debug.hxx>
#endif
#ifndef _COM_SUN_STAR_SDBC_XDATABASEMETADATA_HPP_
#include <com/sun/star/sdbc/XDatabaseMetaData.hpp>
#endif
#ifndef _SVX_DBEXCH_HRC
#include <svx/dbexch.hrc>
#endif
#ifndef _SV_SVAPP_HXX
#include <vcl/svapp.hxx>
#endif

using namespace dbaui;
using namespace ::com::sun::star::sdbc;
using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::datatransfer;

OJoinExchangeData::OJoinExchangeData(OTableWindowListBox* pBox)
    : pListBox(pBox)
    , pEntry(pBox->FirstSelected())
{ }

const ULONG SCROLLING_TIMESPAN = 500;
const long LISTBOX_SCROLLING_AREA = 6;
//==================================================================
// class OTableWindowListBox
//==================================================================
DBG_NAME(OTableWindowListBox);
//------------------------------------------------------------------------------
OTableWindowListBox::OTableWindowListBox( OTableWindow* pParent, const String& rDatabaseName, const String& rTableName ) :
     SvTreeListBox( pParent, WB_HASBUTTONS | WB_BORDER)
    ,m_pTabWin( pParent )
    ,m_aMousePos( Point(0,0) )
    ,m_bReallyScrolled( sal_False )
    ,m_bDragSource( sal_False )
    ,m_nDropEvent(0)
{
    DBG_CTOR(OTableWindowListBox,NULL);
    SetDoubleClickHdl( LINK(this, OTableWindowListBox, DoubleClickHdl) );

    m_aScrollHelper.setUpScrollMethod( LINK(this, OTableWindowListBox, ScrollUpHdl) );
    m_aScrollHelper.setDownScrollMethod( LINK(this, OTableWindowListBox, ScrollDownHdl) );

    SetHighlightRange( );
}

//------------------------------------------------------------------------------
void OTableWindowListBox::dragFinished( )
{
    m_bDragSource = sal_False;
    // first show the error msg when existing
    m_pTabWin->getDesignView()->getController()->showError(m_pTabWin->getDesignView()->getController()->clearOccuredError());
    // second look for ui activities which should happen after d&d
    m_pTabWin->getTableView()->lookForUiActivities();
}

//------------------------------------------------------------------------------
OTableWindowListBox::~OTableWindowListBox()
{
    if (m_nDropEvent)
        Application::RemoveUserEvent(m_nDropEvent);

    m_pTabWin = NULL;
    DBG_DTOR(OTableWindowListBox,NULL);
}

//------------------------------------------------------------------------------
SvLBoxEntry* OTableWindowListBox::GetEntryFromText( const String& rEntryText )
{
    //////////////////////////////////////////////////////////////////////
    // Liste durchiterieren
    SvTreeList* pTreeList = GetModel();
    SvLBoxEntry* pEntry = (SvLBoxEntry*)pTreeList->First();
    OJoinDesignView* pView = m_pTabWin->getDesignView();
    OJoinController* pController = pView->getController();

    BOOL bCase = FALSE;
    try
    {
        Reference<XConnection> xConnection = pController->getConnection();
        if(xConnection.is())
        {
            Reference<XDatabaseMetaData> xMeta = xConnection->getMetaData();
            if(xMeta.is())
                bCase = xMeta->storesMixedCaseQuotedIdentifiers();
        }
        while( pEntry )
        {
            if((bCase ? rEntryText == GetEntryText(pEntry) : rEntryText.EqualsIgnoreCaseAscii(GetEntryText(pEntry))))
            {
                return pEntry;
            }
            pEntry = (SvLBoxEntry*)pTreeList->Next( pEntry );
        }
    }
    catch(SQLException&)
    {
    }

    return NULL;
}

//------------------------------------------------------------------------------
void OTableWindowListBox::NotifyScrolled()
{
    m_bReallyScrolled = TRUE;
}

//------------------------------------------------------------------------------
void OTableWindowListBox::NotifyEndScroll()
{
    if (m_bReallyScrolled)
        // die Verbindungen, die diese Tabelle eventuell hat, muessen neu gezeichnet werden
        m_pTabWin->getTableView()->Invalidate(INVALIDATE_NOCHILDREN);
        // ohne das INVALIDATE_NOCHILDREN wuerden auch alle Tabellen neu gezeichnet werden,
        // sprich : es flackert
    m_bReallyScrolled = FALSE;
}

//------------------------------------------------------------------------------
long OTableWindowListBox::PreNotify(NotifyEvent& rNEvt)
{
    BOOL bHandled = FALSE;
    switch (rNEvt.GetType())
    {
        case EVENT_KEYINPUT:
        {
            const KeyEvent* pKeyEvent = rNEvt.GetKeyEvent();
            const KeyCode& rCode = pKeyEvent->GetKeyCode();

            if (rCode.GetCode() != KEY_RETURN)
            {
                if(m_pTabWin)
                {
                    m_pTabWin->KeyInput(*pKeyEvent);
                    bHandled = TRUE;
                }
                break;
            }

            if (rCode.IsMod1() || rCode.IsMod2() || rCode.IsShift())
                break;
            if (FirstSelected())
                static_cast<OTableWindow*>(Window::GetParent())->OnEntryDoubleClicked(FirstSelected());
        }
        break;
    }

    if (!bHandled)
        return SvTreeListBox::PreNotify(rNEvt);
    return 1L;
}
// -----------------------------------------------------------------------------
void scrollWindow(OTableWindowListBox* _pListBox, const Point& _rPos,sal_Bool _bUp)
{
    SvLBoxEntry* pEntry = _pListBox->GetEntry( _rPos );

    if( pEntry && pEntry != _pListBox->Last() )
    {
        _pListBox->ScrollOutputArea( _bUp ? -1 : 1 );
        pEntry = _pListBox->GetEntry( _rPos );
        _pListBox->Select( pEntry, TRUE );
    }
}
//------------------------------------------------------------------------------
IMPL_LINK( OTableWindowListBox, ScrollUpHdl, SvTreeListBox*, EMPTY_ARG )
{
    scrollWindow(this,m_aMousePos,sal_True);

    return 0;
}

//------------------------------------------------------------------------------
IMPL_LINK( OTableWindowListBox, ScrollDownHdl, SvTreeListBox*, EMPTY_ARG )
{
    scrollWindow(this,m_aMousePos,sal_False);
    return 0;
}

//------------------------------------------------------------------------------
void OTableWindowListBox::StartDrag( sal_Int8 nAction, const Point& rPosPixel )
{
    OJoinTableView* pCont = m_pTabWin->getTableView();
    if (!pCont->getDesignView()->getController()->isReadOnly() && pCont->getDesignView()->getController()->isConnected())
    {
        sal_Bool bFirst = FirstSelected() != First();
        EndSelection();
        if ( !m_pTabWin->GetData()->IsShowAll() || bFirst)
        {
            // eine Beschreibung der Source
            OJoinExchangeData jxdSource(this);
            m_bDragSource = sal_True;
            // in ein Exchange-Objekt packen
            OJoinExchObj* pJoin = new OJoinExchObj(jxdSource);
            Reference< XTransferable > xEnsureDelete(pJoin);
            pJoin->StartDrag(this, DND_ACTION_LINK, this);
        }
    }
}

//------------------------------------------------------------------------------
sal_Int8 OTableWindowListBox::AcceptDrop( const AcceptDropEvent& _rEvt )
{
    sal_Int8 nDND_Action = DND_ACTION_NONE;
    // check the format
    if (!m_bDragSource && OJoinExchObj::isFormatAvailable(GetDataFlavorExVector()))
    {   // don't drop into the window if it's the drag source itself

        // remove the selection if the dragging operation is leaving the window
        if (_rEvt.mbLeaving)
            SelectAll(FALSE);
        else
        {
            // hit test
            m_aMousePos = _rEvt.maPosPixel;
            SvLBoxEntry* pEntry = GetEntry( m_aMousePos );
            if( pEntry )
            {
                m_aScrollHelper.scroll(m_aMousePos,GetOutputSizePixel());
                // Beim Drag automatisch den richtigen Eintrag selektieren
                if ((FirstSelected() != pEntry) || (FirstSelected() && NextSelected(FirstSelected())))
                    SelectAll(FALSE);
                Select(pEntry, TRUE);

                // Auf den ersten Eintrag (*) kann nicht gedroppt werden
                if(!( m_pTabWin->GetData()->IsShowAll() && (pEntry==First()) ))
                    nDND_Action = DND_ACTION_LINK;
            }
        }
    }
    return nDND_Action;
}
// -----------------------------------------------------------------------------

//------------------------------------------------------------------------------
IMPL_LINK( OTableWindowListBox, DropHdl, void *, EMPTY_ARG)
{
    // create the connection
    m_nDropEvent = NULL;
    OSL_ENSURE(m_pTabWin,"No TableWindow!");
    try
    {
        OJoinTableView* pCont = m_pTabWin->getTableView();
        OSL_ENSURE(pCont,"No OJoinTableView!");
        pCont->AddConnection(m_aDropInfo.aSource, m_aDropInfo.aDest);
    }
    catch(const SQLException& e)
    {
        // remember the exception so that we can show them later when d&d is finished
        m_pTabWin->getDesignView()->getController()->setErrorOccured(::dbtools::SQLExceptionInfo(e));
    }
    return 0L;
}
//------------------------------------------------------------------------------
sal_Int8 OTableWindowListBox::ExecuteDrop( const ExecuteDropEvent& _rEvt )
{
    TransferableDataHelper aDropped(_rEvt.maDropEvent.Transferable);
    if (!m_bDragSource && OJoinExchObj::isFormatAvailable(aDropped.GetDataFlavorExVector()))
    {   // don't drop into the window if it's the drag source itself

        m_aDropInfo.aSource = OJoinExchangeData(this);
        m_aDropInfo.aDest   = OJoinExchObj::GetSourceDescription(_rEvt.maDropEvent.Transferable);

        if (m_nDropEvent)
            Application::RemoveUserEvent(m_nDropEvent);
        m_nDropEvent = Application::PostUserEvent(LINK(this, OTableWindowListBox, DropHdl));

        return DND_ACTION_LINK;
    }
    return DND_ACTION_NONE;
}

//------------------------------------------------------------------------------
void OTableWindowListBox::LoseFocus()
{
    if(m_pTabWin)
        m_pTabWin->setActive(sal_False);
    SvTreeListBox::LoseFocus();
}

//------------------------------------------------------------------------------
void OTableWindowListBox::GetFocus()
{
    if(m_pTabWin)
        m_pTabWin->setActive();

    if (GetCurEntry() != NULL)
    {
        if (GetSelectionCount() == 0)
            Select(GetCurEntry(), TRUE);
        else
            ShowFocusRect(FirstSelected());
    }
    SvTreeListBox::GetFocus();

//  if(m_pTabWin)
//      m_pTabWin->GrabFocus();
}

//------------------------------------------------------------------------------
IMPL_LINK( OTableWindowListBox, DoubleClickHdl, SvTreeListBox *, pBox )
{
    // meinem Elter Bescheid sagen
    Window* pParent = Window::GetParent();
    DBG_ASSERT(pParent != NULL, "OTableWindowListBox::DoubleClickHdl : habe kein Parent !");

    static_cast<OTableWindow*>(pParent)->OnEntryDoubleClicked(GetHdlEntry());

    return 0;
}
// -----------------------------------------------------------------------------
void OTableWindowListBox::Command(const CommandEvent& rEvt)
{
    switch (rEvt.GetCommand())
    {
        case COMMAND_CONTEXTMENU:
        {
            static_cast<OTableWindow*>(Window::GetParent())->Command(rEvt);
            break;
        }
        default:
            SvTreeListBox::Command(rEvt);
    }
}
// -----------------------------------------------------------------------------

<commit_msg>#99300# keyEvent corrected<commit_after>/*************************************************************************
 *
 *  $RCSfile: TableWindowListBox.cxx,v $
 *
 *  $Revision: 1.25 $
 *
 *  last change: $Author: oj $ $Date: 2002-05-22 10:52:13 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 by Sun Microsystems, Inc.
 *  901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License version 2.1, as published by the Free Software Foundation.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *  MA  02111-1307  USA
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (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.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an AS IS basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 *
 *  Copyright: 2000 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): _______________________________________
 *
 *
 ************************************************************************/
#ifndef DBAUI_TABLEWINDOWLISTBOX_HXX
#include "TableWindowListBox.hxx"
#endif
#ifndef DBAUI_TABLEWINDOW_HXX
#include "TableWindow.hxx"
#endif
#ifndef DBAUI_QUERYDESIGNVIEW_HXX
#include "QueryDesignView.hxx"
#endif
#ifndef DBAUI_QUERYTABLEVIEW_HXX
#include "QueryTableView.hxx"
#endif
#ifndef DBAUI_QUERYCONTROLLER_HXX
#include "querycontroller.hxx"
#endif
#ifndef DBAUI_JOINEXCHANGE_HXX
#include "JoinExchange.hxx"
#endif
#ifndef _TOOLS_DEBUG_HXX
#include <tools/debug.hxx>
#endif
#ifndef _COM_SUN_STAR_SDBC_XDATABASEMETADATA_HPP_
#include <com/sun/star/sdbc/XDatabaseMetaData.hpp>
#endif
#ifndef _SVX_DBEXCH_HRC
#include <svx/dbexch.hrc>
#endif
#ifndef _SV_SVAPP_HXX
#include <vcl/svapp.hxx>
#endif

using namespace dbaui;
using namespace ::com::sun::star::sdbc;
using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::datatransfer;

OJoinExchangeData::OJoinExchangeData(OTableWindowListBox* pBox)
    : pListBox(pBox)
    , pEntry(pBox->FirstSelected())
{ }

const ULONG SCROLLING_TIMESPAN = 500;
const long LISTBOX_SCROLLING_AREA = 6;
//==================================================================
// class OTableWindowListBox
//==================================================================
DBG_NAME(OTableWindowListBox);
//------------------------------------------------------------------------------
OTableWindowListBox::OTableWindowListBox( OTableWindow* pParent, const String& rDatabaseName, const String& rTableName ) :
     SvTreeListBox( pParent, WB_HASBUTTONS | WB_BORDER)
    ,m_pTabWin( pParent )
    ,m_aMousePos( Point(0,0) )
    ,m_bReallyScrolled( sal_False )
    ,m_bDragSource( sal_False )
    ,m_nDropEvent(0)
{
    DBG_CTOR(OTableWindowListBox,NULL);
    SetDoubleClickHdl( LINK(this, OTableWindowListBox, DoubleClickHdl) );

    m_aScrollHelper.setUpScrollMethod( LINK(this, OTableWindowListBox, ScrollUpHdl) );
    m_aScrollHelper.setDownScrollMethod( LINK(this, OTableWindowListBox, ScrollDownHdl) );

    SetHighlightRange( );
}

//------------------------------------------------------------------------------
void OTableWindowListBox::dragFinished( )
{
    m_bDragSource = sal_False;
    // first show the error msg when existing
    m_pTabWin->getDesignView()->getController()->showError(m_pTabWin->getDesignView()->getController()->clearOccuredError());
    // second look for ui activities which should happen after d&d
    m_pTabWin->getTableView()->lookForUiActivities();
}

//------------------------------------------------------------------------------
OTableWindowListBox::~OTableWindowListBox()
{
    if (m_nDropEvent)
        Application::RemoveUserEvent(m_nDropEvent);

    m_pTabWin = NULL;
    DBG_DTOR(OTableWindowListBox,NULL);
}

//------------------------------------------------------------------------------
SvLBoxEntry* OTableWindowListBox::GetEntryFromText( const String& rEntryText )
{
    //////////////////////////////////////////////////////////////////////
    // Liste durchiterieren
    SvTreeList* pTreeList = GetModel();
    SvLBoxEntry* pEntry = (SvLBoxEntry*)pTreeList->First();
    OJoinDesignView* pView = m_pTabWin->getDesignView();
    OJoinController* pController = pView->getController();

    BOOL bCase = FALSE;
    try
    {
        Reference<XConnection> xConnection = pController->getConnection();
        if(xConnection.is())
        {
            Reference<XDatabaseMetaData> xMeta = xConnection->getMetaData();
            if(xMeta.is())
                bCase = xMeta->storesMixedCaseQuotedIdentifiers();
        }
        while( pEntry )
        {
            if((bCase ? rEntryText == GetEntryText(pEntry) : rEntryText.EqualsIgnoreCaseAscii(GetEntryText(pEntry))))
            {
                return pEntry;
            }
            pEntry = (SvLBoxEntry*)pTreeList->Next( pEntry );
        }
    }
    catch(SQLException&)
    {
    }

    return NULL;
}

//------------------------------------------------------------------------------
void OTableWindowListBox::NotifyScrolled()
{
    m_bReallyScrolled = TRUE;
}

//------------------------------------------------------------------------------
void OTableWindowListBox::NotifyEndScroll()
{
    if (m_bReallyScrolled)
        // die Verbindungen, die diese Tabelle eventuell hat, muessen neu gezeichnet werden
        m_pTabWin->getTableView()->Invalidate(INVALIDATE_NOCHILDREN);
        // ohne das INVALIDATE_NOCHILDREN wuerden auch alle Tabellen neu gezeichnet werden,
        // sprich : es flackert
    m_bReallyScrolled = FALSE;
}

//------------------------------------------------------------------------------
long OTableWindowListBox::PreNotify(NotifyEvent& rNEvt)
{
    BOOL bHandled = FALSE;
    switch (rNEvt.GetType())
    {
        case EVENT_KEYINPUT:
        {
            const KeyEvent* pKeyEvent = rNEvt.GetKeyEvent();
            const KeyCode& rCode = pKeyEvent->GetKeyCode();

            if (rCode.GetCode() != KEY_RETURN)
            {
                if(m_pTabWin)
                {
                    bHandled = m_pTabWin->HandleKeyInput(*pKeyEvent);
                    //  bHandled = TRUE;
                }
                break;
            }

            if (rCode.IsMod1() || rCode.IsMod2() || rCode.IsShift())
                break;
            if (FirstSelected())
                static_cast<OTableWindow*>(Window::GetParent())->OnEntryDoubleClicked(FirstSelected());
        }
        break;
    }

    if (!bHandled)
        return SvTreeListBox::PreNotify(rNEvt);
    return 1L;
}
// -----------------------------------------------------------------------------
void scrollWindow(OTableWindowListBox* _pListBox, const Point& _rPos,sal_Bool _bUp)
{
    SvLBoxEntry* pEntry = _pListBox->GetEntry( _rPos );

    if( pEntry && pEntry != _pListBox->Last() )
    {
        _pListBox->ScrollOutputArea( _bUp ? -1 : 1 );
        pEntry = _pListBox->GetEntry( _rPos );
        _pListBox->Select( pEntry, TRUE );
    }
}
//------------------------------------------------------------------------------
IMPL_LINK( OTableWindowListBox, ScrollUpHdl, SvTreeListBox*, EMPTY_ARG )
{
    scrollWindow(this,m_aMousePos,sal_True);

    return 0;
}

//------------------------------------------------------------------------------
IMPL_LINK( OTableWindowListBox, ScrollDownHdl, SvTreeListBox*, EMPTY_ARG )
{
    scrollWindow(this,m_aMousePos,sal_False);
    return 0;
}

//------------------------------------------------------------------------------
void OTableWindowListBox::StartDrag( sal_Int8 nAction, const Point& rPosPixel )
{
    OJoinTableView* pCont = m_pTabWin->getTableView();
    if (!pCont->getDesignView()->getController()->isReadOnly() && pCont->getDesignView()->getController()->isConnected())
    {
        sal_Bool bFirst = FirstSelected() != First();
        EndSelection();
        if ( !m_pTabWin->GetData()->IsShowAll() || bFirst)
        {
            // eine Beschreibung der Source
            OJoinExchangeData jxdSource(this);
            m_bDragSource = sal_True;
            // in ein Exchange-Objekt packen
            OJoinExchObj* pJoin = new OJoinExchObj(jxdSource);
            Reference< XTransferable > xEnsureDelete(pJoin);
            pJoin->StartDrag(this, DND_ACTION_LINK, this);
        }
    }
}

//------------------------------------------------------------------------------
sal_Int8 OTableWindowListBox::AcceptDrop( const AcceptDropEvent& _rEvt )
{
    sal_Int8 nDND_Action = DND_ACTION_NONE;
    // check the format
    if (!m_bDragSource && OJoinExchObj::isFormatAvailable(GetDataFlavorExVector()))
    {   // don't drop into the window if it's the drag source itself

        // remove the selection if the dragging operation is leaving the window
        if (_rEvt.mbLeaving)
            SelectAll(FALSE);
        else
        {
            // hit test
            m_aMousePos = _rEvt.maPosPixel;
            SvLBoxEntry* pEntry = GetEntry( m_aMousePos );
            if( pEntry )
            {
                m_aScrollHelper.scroll(m_aMousePos,GetOutputSizePixel());
                // Beim Drag automatisch den richtigen Eintrag selektieren
                if ((FirstSelected() != pEntry) || (FirstSelected() && NextSelected(FirstSelected())))
                    SelectAll(FALSE);
                Select(pEntry, TRUE);

                // Auf den ersten Eintrag (*) kann nicht gedroppt werden
                if(!( m_pTabWin->GetData()->IsShowAll() && (pEntry==First()) ))
                    nDND_Action = DND_ACTION_LINK;
            }
        }
    }
    return nDND_Action;
}
// -----------------------------------------------------------------------------

//------------------------------------------------------------------------------
IMPL_LINK( OTableWindowListBox, DropHdl, void *, EMPTY_ARG)
{
    // create the connection
    m_nDropEvent = NULL;
    OSL_ENSURE(m_pTabWin,"No TableWindow!");
    try
    {
        OJoinTableView* pCont = m_pTabWin->getTableView();
        OSL_ENSURE(pCont,"No OJoinTableView!");
        pCont->AddConnection(m_aDropInfo.aSource, m_aDropInfo.aDest);
    }
    catch(const SQLException& e)
    {
        // remember the exception so that we can show them later when d&d is finished
        m_pTabWin->getDesignView()->getController()->setErrorOccured(::dbtools::SQLExceptionInfo(e));
    }
    return 0L;
}
//------------------------------------------------------------------------------
sal_Int8 OTableWindowListBox::ExecuteDrop( const ExecuteDropEvent& _rEvt )
{
    TransferableDataHelper aDropped(_rEvt.maDropEvent.Transferable);
    if (!m_bDragSource && OJoinExchObj::isFormatAvailable(aDropped.GetDataFlavorExVector()))
    {   // don't drop into the window if it's the drag source itself

        m_aDropInfo.aSource = OJoinExchangeData(this);
        m_aDropInfo.aDest   = OJoinExchObj::GetSourceDescription(_rEvt.maDropEvent.Transferable);

        if (m_nDropEvent)
            Application::RemoveUserEvent(m_nDropEvent);
        m_nDropEvent = Application::PostUserEvent(LINK(this, OTableWindowListBox, DropHdl));

        return DND_ACTION_LINK;
    }
    return DND_ACTION_NONE;
}

//------------------------------------------------------------------------------
void OTableWindowListBox::LoseFocus()
{
    if(m_pTabWin)
        m_pTabWin->setActive(sal_False);
    SvTreeListBox::LoseFocus();
}

//------------------------------------------------------------------------------
void OTableWindowListBox::GetFocus()
{
    if(m_pTabWin)
        m_pTabWin->setActive();

    if (GetCurEntry() != NULL)
    {
        if (GetSelectionCount() == 0)
            Select(GetCurEntry(), TRUE);
        else
            ShowFocusRect(FirstSelected());
    }
    SvTreeListBox::GetFocus();

//  if(m_pTabWin)
//      m_pTabWin->GrabFocus();
}

//------------------------------------------------------------------------------
IMPL_LINK( OTableWindowListBox, DoubleClickHdl, SvTreeListBox *, pBox )
{
    // meinem Elter Bescheid sagen
    Window* pParent = Window::GetParent();
    DBG_ASSERT(pParent != NULL, "OTableWindowListBox::DoubleClickHdl : habe kein Parent !");

    static_cast<OTableWindow*>(pParent)->OnEntryDoubleClicked(GetHdlEntry());

    return 0;
}
// -----------------------------------------------------------------------------
void OTableWindowListBox::Command(const CommandEvent& rEvt)
{
    switch (rEvt.GetCommand())
    {
        case COMMAND_CONTEXTMENU:
        {
            static_cast<OTableWindow*>(Window::GetParent())->Command(rEvt);
            break;
        }
        default:
            SvTreeListBox::Command(rEvt);
    }
}
// -----------------------------------------------------------------------------

<|endoftext|>


<commit_before>// Copyright (c) 2015 kamyu. All rights reserved.

#include <iostream>
#include <string>

using std::cout;
using std::endl;
using std::string;

// Thread-Safe, Lazy Initilization
template<typename T>
class Singleton {
 public:
    /**
     * @return: The same instance of this class every time
     */
    static T& instance() {
        // C++ 11 thread-safe local-static-initialization.
        static T *val = new T();

        return *val;
    }

    // Noncopyable.
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;

 private:
    Singleton() {}
    ~Singleton() {}
};

int main() {
    Singleton<string>::instance() = "Hello world!";
    cout << Singleton<string>::instance() << endl;
    return 0;
}
<commit_msg>Update singleton.cpp<commit_after>// Copyright (c) 2015 kamyu. All rights reserved.

#include <iostream>
#include <string>

using std::cout;
using std::endl;
using std::string;

// Thread-Safe, Lazy Initilization
template<typename T>
class Singleton {
 public:
    /**
     * @return: The same instance of this class every time
     */
    static T& instance() {
        // C++ 11 thread-safe local-static-initialization.
        static T *val = new T();

        return *val;
    }

    // Non instanceable.
    Singleton() = delete;
    ~Singleton() = delete;

    // Noncopyable.
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;
};

int main() {
    Singleton<string>::instance() = "Hello world!";
    cout << Singleton<string>::instance() << endl;
    return 0;
}
<|endoftext|>


<commit_before>// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.

#include "fieldsetrepo.h"
#include <vespa/vespalib/text/stringtokenizer.h>
#include <vespa/vespalib/util/exceptions.h>
#include <vespa/vespalib/stllike/asciistream.h>
#include <vespa/document/repo/documenttyperepo.h>
#include <vespa/vespalib/stllike/hash_map.hpp>

using vespalib::StringTokenizer;

namespace document {

namespace {

FieldSet::SP
parseSpecialValues(vespalib::stringref name)
{
    if ((name.size() == 4) && (name[1] == 'i') && (name[2] == 'd') && (name[3] == ']')) {
        return std::make_shared<DocIdOnly>();
    } else if ((name.size() == 5) && (name[1] == 'a') && (name[2] == 'l') && (name[3] == 'l') && (name[4] == ']')) {
        return std::make_shared<AllFields>();
    } else if ((name.size() == 6) && (name[1] == 'n') && (name[2] == 'o') && (name[3] == 'n') && (name[4] == 'e') && (name[5] == ']')) {
        return std::make_shared<NoFields>();
    } else if ((name.size() == 7) && (name[1] == 'd') && (name[2] == 'o') && (name[3] == 'c') && (name[4] == 'i') && (name[5] == 'd') && (name[6] == ']')) {
        return std::make_shared<DocIdOnly>();
    } else {
        throw vespalib::IllegalArgumentException(
                "The only special names (enclosed in '[]') allowed are "
                "id, all, none, not '" + name + "'.");
    }
}

FieldSet::SP
parseFieldCollection(const DocumentTypeRepo& repo,
                     vespalib::stringref docType,
                     vespalib::stringref fieldNames)
{
    const DocumentType* typePtr = repo.getDocumentType(docType);
    if (!typePtr) {
        throw vespalib::IllegalArgumentException("Unknown document type " + docType);
    }
    const DocumentType& type(*typePtr);

    StringTokenizer tokenizer(fieldNames, ",");
    Field::Set::Builder builder;
    for (const auto & token : tokenizer) {
        const DocumentType::FieldSet * fs = type.getFieldSet(token);
        if (fs) {
            for (const auto & fieldName : fs->getFields()) {
                builder.add(&type.getField(fieldName));
            }
        } else {
            builder.add(&type.getField(token));
        }
    }
    return std::make_shared<FieldCollection>(type, builder.build());
}

}

FieldSet::SP
FieldSetRepo::parse(const DocumentTypeRepo& repo, vespalib::stringref str)
{
    if (str[0] == '[') {
        return parseSpecialValues(str);
    } else {
        StringTokenizer tokenizer(str, ":");
        if (tokenizer.size() != 2) {
            throw vespalib::IllegalArgumentException(
                    "The field set list must consist of a document type, "
                    "then a colon (:), then a comma-separated list of field names");
        }

        return parseFieldCollection(repo, tokenizer[0], tokenizer[1]);
    }
}

vespalib::string
FieldSetRepo::serialize(const FieldSet& fieldSet)
{
    switch (fieldSet.getType()) {
        case FieldSet::Type::FIELD:
            return static_cast<const Field&>(fieldSet).getName();
        case FieldSet::Type::SET: {
            const auto & collection = static_cast<const FieldCollection&>(fieldSet);

            vespalib::asciistream stream;
            stream << collection.getDocumentType().getName() << ":";
            bool first = true;
            for (const Field * field : collection.getFields()) {
                if (first) {
                    first = false;
                } else {
                    stream << ",";
                }
                stream << field->getName();
            }

            return stream.str();
        }
        case FieldSet::Type::ALL:
            return AllFields::NAME;
        case FieldSet::Type::NONE:
            return NoFields::NAME;
        case FieldSet::Type::DOCID:
            return DocIdOnly::NAME;
        default:
            return "";
    }
}

FieldSetRepo::FieldSetRepo(const DocumentTypeRepo& repo)
    : _doumentTyperepo(repo),
      _configuredFieldSets()
{
    repo.forEachDocumentType(*vespalib::makeClosure(this, &FieldSetRepo::configureDocumentType));
}
FieldSetRepo::~FieldSetRepo() = default;

void
FieldSetRepo::configureDocumentType(const DocumentType & documentType) {
    for (const auto & entry : documentType.getFieldSets()) {
        vespalib::string fieldSetName(documentType.getName());
        fieldSetName.append(':').append(entry.first);
        _configuredFieldSets[fieldSetName] = parse(_doumentTyperepo, fieldSetName);
    }
}
FieldSet::SP
FieldSetRepo::getFieldSet(vespalib::stringref fieldSetString) const {
    auto found = _configuredFieldSets.find(fieldSetString);
    if (found != _configuredFieldSets.end()) {
        return found->second;
    }
    return parse(_doumentTyperepo, fieldSetString);
}

}

<commit_msg>Expect and handle field not found exception.<commit_after>// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.

#include "fieldsetrepo.h"
#include <vespa/vespalib/text/stringtokenizer.h>
#include <vespa/vespalib/util/exceptions.h>
#include <vespa/vespalib/stllike/asciistream.h>
#include <vespa/document/repo/documenttyperepo.h>
#include <vespa/document/base/exceptions.h>
#include <vespa/vespalib/stllike/hash_map.hpp>

#include <vespa/log/log.h>
LOG_SETUP(".document.fieldset.fieldsetrepo");

using vespalib::StringTokenizer;

namespace document {

namespace {

FieldSet::SP
parseSpecialValues(vespalib::stringref name)
{
    if ((name.size() == 4) && (name[1] == 'i') && (name[2] == 'd') && (name[3] == ']')) {
        return std::make_shared<DocIdOnly>();
    } else if ((name.size() == 5) && (name[1] == 'a') && (name[2] == 'l') && (name[3] == 'l') && (name[4] == ']')) {
        return std::make_shared<AllFields>();
    } else if ((name.size() == 6) && (name[1] == 'n') && (name[2] == 'o') && (name[3] == 'n') && (name[4] == 'e') && (name[5] == ']')) {
        return std::make_shared<NoFields>();
    } else if ((name.size() == 7) && (name[1] == 'd') && (name[2] == 'o') && (name[3] == 'c') && (name[4] == 'i') && (name[5] == 'd') && (name[6] == ']')) {
        return std::make_shared<DocIdOnly>();
    } else {
        throw vespalib::IllegalArgumentException(
                "The only special names (enclosed in '[]') allowed are "
                "id, all, none, not '" + name + "'.");
    }
}

FieldSet::SP
parseFieldCollection(const DocumentTypeRepo& repo,
                     vespalib::stringref docType,
                     vespalib::stringref fieldNames)
{
    const DocumentType* typePtr = repo.getDocumentType(docType);
    if (!typePtr) {
        throw vespalib::IllegalArgumentException("Unknown document type " + docType);
    }
    const DocumentType& type(*typePtr);

    StringTokenizer tokenizer(fieldNames, ",");
    Field::Set::Builder builder;
    for (const auto & token : tokenizer) {
        const DocumentType::FieldSet * fs = type.getFieldSet(token);
        if (fs) {
            for (const auto & fieldName : fs->getFields()) {
                builder.add(&type.getField(fieldName));
            }
        } else {
            builder.add(&type.getField(token));
        }
    }
    return std::make_shared<FieldCollection>(type, builder.build());
}

}

FieldSet::SP
FieldSetRepo::parse(const DocumentTypeRepo& repo, vespalib::stringref str)
{
    if (str[0] == '[') {
        return parseSpecialValues(str);
    } else {
        StringTokenizer tokenizer(str, ":");
        if (tokenizer.size() != 2) {
            throw vespalib::IllegalArgumentException(
                    "The field set list must consist of a document type, "
                    "then a colon (:), then a comma-separated list of field names");
        }

        return parseFieldCollection(repo, tokenizer[0], tokenizer[1]);
    }
}

vespalib::string
FieldSetRepo::serialize(const FieldSet& fieldSet)
{
    switch (fieldSet.getType()) {
        case FieldSet::Type::FIELD:
            return static_cast<const Field&>(fieldSet).getName();
        case FieldSet::Type::SET: {
            const auto & collection = static_cast<const FieldCollection&>(fieldSet);

            vespalib::asciistream stream;
            stream << collection.getDocumentType().getName() << ":";
            bool first = true;
            for (const Field * field : collection.getFields()) {
                if (first) {
                    first = false;
                } else {
                    stream << ",";
                }
                stream << field->getName();
            }

            return stream.str();
        }
        case FieldSet::Type::ALL:
            return AllFields::NAME;
        case FieldSet::Type::NONE:
            return NoFields::NAME;
        case FieldSet::Type::DOCID:
            return DocIdOnly::NAME;
        default:
            return "";
    }
}

FieldSetRepo::FieldSetRepo(const DocumentTypeRepo& repo)
    : _doumentTyperepo(repo),
      _configuredFieldSets()
{
    repo.forEachDocumentType(*vespalib::makeClosure(this, &FieldSetRepo::configureDocumentType));
}
FieldSetRepo::~FieldSetRepo() = default;

void
FieldSetRepo::configureDocumentType(const DocumentType & documentType) {
    for (const auto & entry : documentType.getFieldSets()) {
        vespalib::string fieldSetName(documentType.getName());
        fieldSetName.append(':').append(entry.first);
        try {
            auto fieldset = parse(_doumentTyperepo, fieldSetName);
            _configuredFieldSets[fieldSetName] = std::move(fieldset);
        } catch (const FieldNotFoundException & ex) {
            LOG(warning, "Did not find field %s in configured fieldset %s, will default to NO fields.",ex.getFieldName().c_str(), fieldSetName.c_str());
            _configuredFieldSets[fieldSetName] = std::make_shared<NoFields>();
        }
    }
}
FieldSet::SP
FieldSetRepo::getFieldSet(vespalib::stringref fieldSetString) const {
    auto found = _configuredFieldSets.find(fieldSetString);
    if (found != _configuredFieldSets.end()) {
        return found->second;
    }
    try {
        return parse(_doumentTyperepo, fieldSetString);
    } catch (const FieldNotFoundException & ex) {
        LOG(warning, "Did not find field %s in configured fieldset %s, will default to NO fields.",ex.getFieldName().c_str(), vespalib::string(fieldSetString).c_str());
        return std::make_shared<NoFields>();
    }
}

}

<|endoftext|>


<commit_before>#include <tinyxml.h>
#include <dirent.h>
#include <sys/stat.h>
#include <string>
#include <list>
#include <vector>
#include <unordered_map>
#include <functional>
#include <algorithm>
#include "zone.h"
#include "world.h"
#include "room.h"
#include "staticObject.h"
#include "event.h"
#include "utils.h"
#include "serializationHelpers.hpp"

static int zone_saves;

Zone::Zone()
{
    _vnumrange.min = 0;
    _vnumrange.max = 0;
    _goldbonus = 0;
    _expbonus = 0;
    _resetfreq = 240;
    _resetmsg = "With a pop, the area resets around you.";
    _lastreset=time(NULL);
    _creation = time(NULL);
    _opened = 0;
    _flags = 0;
}
Zone::~Zone()
{
    for(auto it: _virtualobjs)
        {
            delete it;
        }

    for (auto it: _objects)
        {
            delete it;
        }

    for (auto it: _roomobjs)
        {
            delete it;
        }

    for (auto it: _mobobjs)
        {
            delete it;
        }
}

std::string Zone::GetName() const
{
    return _name;
}
void Zone::SetName(const std::string &name)
{
    _name=name;
}
void Zone::SetRange(int min, int max)
{
    _vnumrange.min = min;
    _vnumrange.max = max;
}
int  Zone::GetMinVnum()
{
    return _vnumrange.min;
}
int  Zone::GetMaxVnum()
{
    return _vnumrange.max;
}
Room* Zone::AddRoom()
{
    World* world = World::GetPtr();
    Room* room = NULL;
    VNUM num = 0;

    if (_rnums.empty())
        {
            CalculateVnumRanges();
            if (_rnums.empty())
                {
                    throw(std::runtime_error("No more vnums available"));
                }
        }

    room = new Room();
    num = _rnums.top();
    _rnums.pop();
    room->SetOnum(num);
    room->SetZone(this);
    _rooms[num] = room;
    _roomobjs.push_back(room);
    world->AddRoom(room);
    return room;
}
Room* Zone::AddRoom(VNUM num)
{
    World* world = World::GetPtr();
    VNUM cur = 0;
    std::stack<VNUM> temp;
    Room* room = NULL;
    BOOL found = false;

    if (_rnums.empty())
        {
            CalculateVnumRanges();
            if (_rnums.empty())
                {
                    throw(std::runtime_error("No more vnums available"));
                }
        }

    room = new Room();
    while (!_rnums.empty())
        {
            cur = _rnums.top();
            _rnums.pop();
            if (cur == num)
                {
                    found = true;
                    continue;
                }
            temp.push(cur);
        }
    if (!found)
        {
            throw(std::runtime_error("That num wasn't found."));
        }

    _rnums = temp;
    room->SetOnum(num);
    room->SetZone(this);
    _rooms[num] = room;
    _roomobjs.push_back(room);
    world->AddRoom(room);
    return room;
}
BOOL Zone::RemoveRoom(VNUM num)
{
    std::vector<Room*>::iterator it, itEnd;

    if (_rooms.count(num))
        {
            itEnd = _roomobjs.end();
            for (it = _roomobjs.begin(); it != itEnd; ++it)
                {
                    if ((*it)->GetOnum() == num)
                        {
                            _roomobjs.erase(it);
                            delete _rooms[num];
                            _rooms.erase(num);
                            return true;
                        }
                }
        }

    return false;
}
void Zone::GetRooms(std::vector<Room*> *rooms)
{
    std::copy(_roomobjs.begin(), _roomobjs.end(), std::back_inserter(*rooms));
}
BOOL Zone::RoomExists(VNUM num)
{
    return (_rooms.count(num) ? true : false);
}

StaticObject* Zone::AddVirtual()
{
    World* world = World::GetPtr();
    StaticObject* obj = NULL;
    VNUM num = 0;

    if (_onums.empty())
        {
            CalculateVnumRanges();
            if (_onums.empty())
                {
                    throw(std::runtime_error("No more vnums available"));
                }
        }

    obj = new StaticObject();
    num = _onums.top();
    _onums.pop();
    obj->SetOnum(num);
    _virtuals[num] = obj;
    _virtualobjs.push_back(obj);
    world->AddVirtual(obj);
    return obj;
}
BOOL Zone::RemoveVirtual(VNUM num)
{
    std::vector<StaticObject*>::iterator it, itEnd;

    itEnd = _virtualobjs.end();
    for (it = _virtualobjs.begin(); it != itEnd; ++it)
        {
            if ((*it)->GetOnum() == num)
                {
                    _virtualobjs.erase(it);
                    delete _virtualobjs[num];
                    _virtuals.erase(num);
                    return true;
                }
        }

    return false;
}
StaticObject* Zone::GetVirtual(VNUM num)
{
    if (VirtualExists(num))
        {
            return _virtuals[num];
        }

    return nullptr;
}
void Zone::GetVirtuals(std::vector<StaticObject*>* objects)
{
    for (auto it: _virtuals)
        {
            objects->push_back(it.second);
        }
}
BOOL Zone::VirtualExists(VNUM num)
{
    return (_virtuals.count(num) ? true : false);
}

Npc* Zone::AddNpc()
{
    Npc* mob = nullptr;
    VNUM num = 0;

    if (_mnums.empty())
        {
            CalculateVnumRanges();
            if (_mnums.empty())
                {
                    throw(std::runtime_error("No more vnums available"));
                }
        }

    mob = new Npc();
    num = _mnums.top();
    _mnums.pop();
    mob->SetOnum(num);
    _mobs[num] = mob;
    _mobobjs.push_back(mob);
    return mob;
}
BOOL Zone::RemoveNpc(VNUM num)
{
    std::vector<Npc*>::iterator it, itEnd;

    itEnd = _mobobjs.end();
    for (it = _mobobjs.begin(); it != itEnd; ++it)
        {
            if ((*it)->GetOnum() == num)
                {
                    _mobobjs.erase(it);
                    delete _mobs[num];
                    _mobs.erase(num);
                    return true;
                }
        }

    return false;
}
Npc* Zone::GetNpc(VNUM num)
{
    if (NpcExists(num))
        {
            return _mobs[num];
        }

    return nullptr;
}
void Zone::GetNpcs(std::vector<Npc*>* npcs)
{
    std::copy(_mobobjs.begin(), _mobobjs.end(), std::back_inserter(*npcs));
}
BOOL Zone::NpcExists(VNUM num)
{
    return (_mobs.count(num) ? true : false);
}
Npc* Zone::CreateNpc(VNUM num, Room* origin)
{
    Npc* templ = GetNpc(num);
    Npc* ret = nullptr;

    if (!templ)
        {
            return nullptr;
        }

    ret = new Npc();
    templ->Copy(ret);
    ret->SetOrigin(origin);
    ret->EnterGame();
    ret->InitializeUuid();
    if (!ret->MoveTo(origin))
        {
            delete ret;
            return nullptr;
        }

    return ret;
}
void Zone::CalculateVnumRanges()

{
    int i = 0; //for counter

//find all empty virtual objects.
    for (i = _vnumrange.min; i != _vnumrange.max; ++i)
        {
            if (!VirtualExists(i))
                {
                    if (_onums.size() == VNUMKEEP)
                        {
                            break;
                        }
                    _onums.push(i);
                }
        }

//find all empty room objects.
    for (i = _vnumrange.min; i != _vnumrange.max; ++i)
        {
            if (!RoomExists(i))
                {
                    if (_rnums.size() == VNUMKEEP)
                        {
                            break;
                        }
                    _rnums.push(i);
                }
        }

//find all empty npc objects
    for (i = _vnumrange.min; i != _vnumrange.max; ++i)
        {
            if (_mnums.size() == VNUMKEEP)
                {
                    break;
                }
            if (!NpcExists(i))
                {
                    _mnums.push(i);
                }
        }
}

void Zone::Update()
{
}

void Zone::Serialize(TiXmlElement* root)
{
    root->SetAttribute("name", _name.c_str());
    root->SetAttribute("flags", _flags);
    root->SetAttribute("creation", _creation);
    root->SetAttribute("opened", _opened);
    root->SetAttribute("resetmsg", _resetmsg.c_str());
    root->SetAttribute("resetfreq", _resetfreq);
    root->SetAttribute("expbonus", _expbonus);
    root->SetAttribute("goldbonus", _goldbonus);
    root->SetAttribute("minvnum", _vnumrange.min);
    root->SetAttribute("maxvnum", _vnumrange.max);

    SerializeList<StaticObject, std::vector<StaticObject*>, std::vector<StaticObject*>::iterator>("vobjes", "vobj", root, _virtualobjs);
    SerializeList<Room, std::vector<Room*>, std::vector<Room*>::iterator>("rooms", "room", root, _roomobjs);
    SerializeList<Npc, std::vector<Npc*>, std::vector<Npc*>::iterator>("npcs", "npc", root, _mobobjs);
}
void Zone::Deserialize(TiXmlElement* zone)
{
    unsigned int u = 0;
    World* world = World::GetPtr();

    _name = zone->Attribute("name");
    zone->Attribute("flags", &_flags);
    zone->Attribute("creation", &u);
    _creation = u;
    zone->Attribute("opened", &u);
    _opened = u;
    _resetmsg = zone->Attribute("resetmsg");
    zone->Attribute("resetfreq", &_resetfreq);
    zone->Attribute("expbonus", &_expbonus);
    zone->Attribute("goldbonus", &_goldbonus);
    zone->Attribute("minvnum", &(_vnumrange.min));
    zone->Attribute("maxvnum", &(_vnumrange.max));

    DeserializeList<StaticObject, std::vector<StaticObject*> >(zone, "vobjes", _virtualobjs);
    DeserializeList<Room, std::vector<Room*> >(zone, "rooms", _roomobjs);
    DeserializeList<Npc, std::vector<Npc*> >(zone, "npcs", _mobobjs);

    for (auto it: _roomobjs)
        {
            world->AddRoom(it);
            _rooms[it->GetOnum()] = it;
            _roomobjs->push_back(it);
            it->SetZone(this);
        }

    for (auto it: _virtualobjs)
        {
            _virtuals[it->GetOnum()] = it;
            _virtualobjs->push_back(it);
            world->AddVirtual(it);
        }
}

BOOL InitializeZones()
{
    World* world = World::GetPtr();
    point p;
    struct stat FInfo;

    world->WriteLog("Initializing areas.");
    if ((stat(AREA_STARTFILE,&FInfo))!=-1)
        {
            Zone::LoadZones();
        }
    else
        {
#ifdef NO_INIT_DEFAULTS
            world->WriteLog("No area file exists, and NO_INIT_DEFAULTS was enabled, exiting.");
            return false;
        }
#else
            world->WriteLog("No area found, creating default.");
//no zones and rooms exist, create a first zone/room.
            Zone*zone=new Zone();
            if (!zone)
                {
                    return false;
                }
            zone->SetName("Start");
            if (!world->AddZone(zone))
                {
                    return false;
                }
            zone->SetRange(1, 100);
            zone->CalculateVnumRanges();
            Room* room = zone->AddRoom(ROOM_START);
            room->SetName("A blank room");
            room->SetCoord(p);
            if (!Zone::SaveZones())
                {
                    return false;
                }
        }
#endif

    zone_saves = 0;
    world->events.AddCallback("WorldPulse",
                              std::bind(&Zone::Autosave, std::placeholders::_1, std::placeholders::_2));
    world->events.AddCallback("Shutdown",
                              std::bind(&Zone::Shutdown, std::placeholders::_1, std::placeholders::_2));
    world->events.AddCallback("Copyover",
                              std::bind(&Zone::Shutdown, std::placeholders::_1, std::placeholders::_2));
    return true;
}

BOOL Zone::SaveZones()
{
    World* world = World::GetPtr();
    std::vector<Zone*> *zones=new std::vector<Zone*>();
    std::string path;

    world->GetZones(zones);
    if (zones->size())
        {
            for (Zone* zone:*zones)
                {
                    TiXmlDocument doc;
                    TiXmlElement* zelement = new TiXmlElement("zone");
                    TiXmlDeclaration *decl = new TiXmlDeclaration("1.0", "", "");
                    doc.LinkEndChild(decl);
                    zone->Serialize(zelement);
                    doc.LinkEndChild(zelement);
                    path = AREA_DIR+zone->GetName();
                    Lower(path);
                    doc.SaveFile(path.c_str());
                }
        }
    delete zones;
    return true;
}
BOOL Zone::LoadZones()
{
    World* world = World::GetPtr();
    Zone* zone=nullptr;
    TiXmlElement* root = nullptr;
    TiXmlNode* node = nullptr;
    dirent* cdir = NULL;
    DIR* dir = opendir(AREA_DIR);

    if (!dir)
        {
            return false;
        }

    while ((cdir = readdir(dir)))
        {
            if (cdir->d_name[0] == '.')
                {
                    continue;
                }

            TiXmlDocument doc((std::string(AREA_DIR)+cdir->d_name).c_str());
            if (!doc.LoadFile())
                {
                    closedir(dir);
                    return false;
                }

            zone = new Zone();
            node = doc.FirstChild("zone");
            if (!node)
                {
                    closedir(dir);
                    return false;
                }

            root = node->ToElement();
            zone->Deserialize(root);
            world->AddZone(zone);
        }
    return true;
}

CEVENT(Zone, Autosave)
{
    zone_saves++;
    if (zone_saves >= 100)
        {
            Zone::SaveZones();
            zone_saves = 0;
        }
}
CEVENT(Zone, Shutdown)
{
    Zone::SaveZones();
}
<commit_msg>More bugfixes on zone deserialization.<commit_after>#include <tinyxml.h>
#include <dirent.h>
#include <sys/stat.h>
#include <string>
#include <list>
#include <vector>
#include <unordered_map>
#include <functional>
#include <algorithm>
#include "zone.h"
#include "world.h"
#include "room.h"
#include "staticObject.h"
#include "event.h"
#include "utils.h"
#include "serializationHelpers.hpp"

static int zone_saves;

Zone::Zone()
{
    _vnumrange.min = 0;
    _vnumrange.max = 0;
    _goldbonus = 0;
    _expbonus = 0;
    _resetfreq = 240;
    _resetmsg = "With a pop, the area resets around you.";
    _lastreset=time(NULL);
    _creation = time(NULL);
    _opened = 0;
    _flags = 0;
}
Zone::~Zone()
{
    for(auto it: _virtualobjs)
        {
            delete it;
        }

    for (auto it: _objects)
        {
            delete it;
        }

    for (auto it: _roomobjs)
        {
            delete it;
        }

    for (auto it: _mobobjs)
        {
            delete it;
        }
}

std::string Zone::GetName() const
{
    return _name;
}
void Zone::SetName(const std::string &name)
{
    _name=name;
}
void Zone::SetRange(int min, int max)
{
    _vnumrange.min = min;
    _vnumrange.max = max;
}
int  Zone::GetMinVnum()
{
    return _vnumrange.min;
}
int  Zone::GetMaxVnum()
{
    return _vnumrange.max;
}
Room* Zone::AddRoom()
{
    World* world = World::GetPtr();
    Room* room = NULL;
    VNUM num = 0;

    if (_rnums.empty())
        {
            CalculateVnumRanges();
            if (_rnums.empty())
                {
                    throw(std::runtime_error("No more vnums available"));
                }
        }

    room = new Room();
    num = _rnums.top();
    _rnums.pop();
    room->SetOnum(num);
    room->SetZone(this);
    _rooms[num] = room;
    _roomobjs.push_back(room);
    world->AddRoom(room);
    return room;
}
Room* Zone::AddRoom(VNUM num)
{
    World* world = World::GetPtr();
    VNUM cur = 0;
    std::stack<VNUM> temp;
    Room* room = NULL;
    BOOL found = false;

    if (_rnums.empty())
        {
            CalculateVnumRanges();
            if (_rnums.empty())
                {
                    throw(std::runtime_error("No more vnums available"));
                }
        }

    room = new Room();
    while (!_rnums.empty())
        {
            cur = _rnums.top();
            _rnums.pop();
            if (cur == num)
                {
                    found = true;
                    continue;
                }
            temp.push(cur);
        }
    if (!found)
        {
            throw(std::runtime_error("That num wasn't found."));
        }

    _rnums = temp;
    room->SetOnum(num);
    room->SetZone(this);
    _rooms[num] = room;
    _roomobjs.push_back(room);
    world->AddRoom(room);
    return room;
}
BOOL Zone::RemoveRoom(VNUM num)
{
    std::vector<Room*>::iterator it, itEnd;

    if (_rooms.count(num))
        {
            itEnd = _roomobjs.end();
            for (it = _roomobjs.begin(); it != itEnd; ++it)
                {
                    if ((*it)->GetOnum() == num)
                        {
                            _roomobjs.erase(it);
                            delete _rooms[num];
                            _rooms.erase(num);
                            return true;
                        }
                }
        }

    return false;
}
void Zone::GetRooms(std::vector<Room*> *rooms)
{
    std::copy(_roomobjs.begin(), _roomobjs.end(), std::back_inserter(*rooms));
}
BOOL Zone::RoomExists(VNUM num)
{
    return (_rooms.count(num) ? true : false);
}

StaticObject* Zone::AddVirtual()
{
    World* world = World::GetPtr();
    StaticObject* obj = NULL;
    VNUM num = 0;

    if (_onums.empty())
        {
            CalculateVnumRanges();
            if (_onums.empty())
                {
                    throw(std::runtime_error("No more vnums available"));
                }
        }

    obj = new StaticObject();
    num = _onums.top();
    _onums.pop();
    obj->SetOnum(num);
    _virtuals[num] = obj;
    _virtualobjs.push_back(obj);
    world->AddVirtual(obj);
    return obj;
}
BOOL Zone::RemoveVirtual(VNUM num)
{
    std::vector<StaticObject*>::iterator it, itEnd;

    itEnd = _virtualobjs.end();
    for (it = _virtualobjs.begin(); it != itEnd; ++it)
        {
            if ((*it)->GetOnum() == num)
                {
                    _virtualobjs.erase(it);
                    delete _virtualobjs[num];
                    _virtuals.erase(num);
                    return true;
                }
        }

    return false;
}
StaticObject* Zone::GetVirtual(VNUM num)
{
    if (VirtualExists(num))
        {
            return _virtuals[num];
        }

    return nullptr;
}
void Zone::GetVirtuals(std::vector<StaticObject*>* objects)
{
    for (auto it: _virtuals)
        {
            objects->push_back(it.second);
        }
}
BOOL Zone::VirtualExists(VNUM num)
{
    return (_virtuals.count(num) ? true : false);
}

Npc* Zone::AddNpc()
{
    Npc* mob = nullptr;
    VNUM num = 0;

    if (_mnums.empty())
        {
            CalculateVnumRanges();
            if (_mnums.empty())
                {
                    throw(std::runtime_error("No more vnums available"));
                }
        }

    mob = new Npc();
    num = _mnums.top();
    _mnums.pop();
    mob->SetOnum(num);
    _mobs[num] = mob;
    _mobobjs.push_back(mob);
    return mob;
}
BOOL Zone::RemoveNpc(VNUM num)
{
    std::vector<Npc*>::iterator it, itEnd;

    itEnd = _mobobjs.end();
    for (it = _mobobjs.begin(); it != itEnd; ++it)
        {
            if ((*it)->GetOnum() == num)
                {
                    _mobobjs.erase(it);
                    delete _mobs[num];
                    _mobs.erase(num);
                    return true;
                }
        }

    return false;
}
Npc* Zone::GetNpc(VNUM num)
{
    if (NpcExists(num))
        {
            return _mobs[num];
        }

    return nullptr;
}
void Zone::GetNpcs(std::vector<Npc*>* npcs)
{
    std::copy(_mobobjs.begin(), _mobobjs.end(), std::back_inserter(*npcs));
}
BOOL Zone::NpcExists(VNUM num)
{
    return (_mobs.count(num) ? true : false);
}
Npc* Zone::CreateNpc(VNUM num, Room* origin)
{
    Npc* templ = GetNpc(num);
    Npc* ret = nullptr;

    if (!templ)
        {
            return nullptr;
        }

    ret = new Npc();
    templ->Copy(ret);
    ret->SetOrigin(origin);
    ret->EnterGame();
    ret->InitializeUuid();
    if (!ret->MoveTo(origin))
        {
            delete ret;
            return nullptr;
        }

    return ret;
}
void Zone::CalculateVnumRanges()

{
    int i = 0; //for counter

//find all empty virtual objects.
    for (i = _vnumrange.min; i != _vnumrange.max; ++i)
        {
            if (!VirtualExists(i))
                {
                    if (_onums.size() == VNUMKEEP)
                        {
                            break;
                        }
                    _onums.push(i);
                }
        }

//find all empty room objects.
    for (i = _vnumrange.min; i != _vnumrange.max; ++i)
        {
            if (!RoomExists(i))
                {
                    if (_rnums.size() == VNUMKEEP)
                        {
                            break;
                        }
                    _rnums.push(i);
                }
        }

//find all empty npc objects
    for (i = _vnumrange.min; i != _vnumrange.max; ++i)
        {
            if (_mnums.size() == VNUMKEEP)
                {
                    break;
                }
            if (!NpcExists(i))
                {
                    _mnums.push(i);
                }
        }
}

void Zone::Update()
{
}

void Zone::Serialize(TiXmlElement* root)
{
    root->SetAttribute("name", _name.c_str());
    root->SetAttribute("flags", _flags);
    root->SetAttribute("creation", _creation);
    root->SetAttribute("opened", _opened);
    root->SetAttribute("resetmsg", _resetmsg.c_str());
    root->SetAttribute("resetfreq", _resetfreq);
    root->SetAttribute("expbonus", _expbonus);
    root->SetAttribute("goldbonus", _goldbonus);
    root->SetAttribute("minvnum", _vnumrange.min);
    root->SetAttribute("maxvnum", _vnumrange.max);

    SerializeList<StaticObject, std::vector<StaticObject*>, std::vector<StaticObject*>::iterator>("vobjes", "vobj", root, _virtualobjs);
    SerializeList<Room, std::vector<Room*>, std::vector<Room*>::iterator>("rooms", "room", root, _roomobjs);
    SerializeList<Npc, std::vector<Npc*>, std::vector<Npc*>::iterator>("npcs", "npc", root, _mobobjs);
}
void Zone::Deserialize(TiXmlElement* zone)
{
    unsigned int u = 0;
    World* world = World::GetPtr();

    _name = zone->Attribute("name");
    zone->Attribute("flags", &_flags);
    zone->Attribute("creation", &u);
    _creation = u;
    zone->Attribute("opened", &u);
    _opened = u;
    _resetmsg = zone->Attribute("resetmsg");
    zone->Attribute("resetfreq", &_resetfreq);
    zone->Attribute("expbonus", &_expbonus);
    zone->Attribute("goldbonus", &_goldbonus);
    zone->Attribute("minvnum", &(_vnumrange.min));
    zone->Attribute("maxvnum", &(_vnumrange.max));

    DeserializeList<StaticObject, std::vector<StaticObject*> >(zone, "vobjes", _virtualobjs);
    DeserializeList<Room, std::vector<Room*> >(zone, "rooms", _roomobjs);
    DeserializeList<Npc, std::vector<Npc*> >(zone, "npcs", _mobobjs);

    for (auto it: _roomobjs)
        {
            world->AddRoom(it);
            _rooms[it->GetOnum()] = it;
            it->SetZone(this);
        }

    for (auto it: _virtualobjs)
        {
            _virtuals[it->GetOnum()] = it;
            world->AddVirtual(it);
        }
}

BOOL InitializeZones()
{
    World* world = World::GetPtr();
    point p;
    struct stat FInfo;

    world->WriteLog("Initializing areas.");
    if ((stat(AREA_STARTFILE,&FInfo))!=-1)
        {
            Zone::LoadZones();
        }
    else
        {
#ifdef NO_INIT_DEFAULTS
            world->WriteLog("No area file exists, and NO_INIT_DEFAULTS was enabled, exiting.");
            return false;
        }
#else
            world->WriteLog("No area found, creating default.");
//no zones and rooms exist, create a first zone/room.
            Zone*zone=new Zone();
            if (!zone)
                {
                    return false;
                }
            zone->SetName("Start");
            if (!world->AddZone(zone))
                {
                    return false;
                }
            zone->SetRange(1, 100);
            zone->CalculateVnumRanges();
            Room* room = zone->AddRoom(ROOM_START);
            room->SetName("A blank room");
            room->SetCoord(p);
            if (!Zone::SaveZones())
                {
                    return false;
                }
        }
#endif

    zone_saves = 0;
    world->events.AddCallback("WorldPulse",
                              std::bind(&Zone::Autosave, std::placeholders::_1, std::placeholders::_2));
    world->events.AddCallback("Shutdown",
                              std::bind(&Zone::Shutdown, std::placeholders::_1, std::placeholders::_2));
    world->events.AddCallback("Copyover",
                              std::bind(&Zone::Shutdown, std::placeholders::_1, std::placeholders::_2));
    return true;
}

BOOL Zone::SaveZones()
{
    World* world = World::GetPtr();
    std::vector<Zone*> *zones=new std::vector<Zone*>();
    std::string path;

    world->GetZones(zones);
    if (zones->size())
        {
            for (Zone* zone:*zones)
                {
                    TiXmlDocument doc;
                    TiXmlElement* zelement = new TiXmlElement("zone");
                    TiXmlDeclaration *decl = new TiXmlDeclaration("1.0", "", "");
                    doc.LinkEndChild(decl);
                    zone->Serialize(zelement);
                    doc.LinkEndChild(zelement);
                    path = AREA_DIR+zone->GetName();
                    Lower(path);
                    doc.SaveFile(path.c_str());
                }
        }
    delete zones;
    return true;
}
BOOL Zone::LoadZones()
{
    World* world = World::GetPtr();
    Zone* zone=nullptr;
    TiXmlElement* root = nullptr;
    TiXmlNode* node = nullptr;
    dirent* cdir = NULL;
    DIR* dir = opendir(AREA_DIR);

    if (!dir)
        {
            return false;
        }

    while ((cdir = readdir(dir)))
        {
            if (cdir->d_name[0] == '.')
                {
                    continue;
                }

            TiXmlDocument doc((std::string(AREA_DIR)+cdir->d_name).c_str());
            if (!doc.LoadFile())
                {
                    closedir(dir);
                    return false;
                }

            zone = new Zone();
            node = doc.FirstChild("zone");
            if (!node)
                {
                    closedir(dir);
                    return false;
                }

            root = node->ToElement();
            zone->Deserialize(root);
            world->AddZone(zone);
        }
    return true;
}

CEVENT(Zone, Autosave)
{
    zone_saves++;
    if (zone_saves >= 100)
        {
            Zone::SaveZones();
            zone_saves = 0;
        }
}
CEVENT(Zone, Shutdown)
{
    Zone::SaveZones();
}
<|endoftext|>


<commit_before>/*
 * Copyright 2010 Google Inc.
 *
 * Licensed 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.
 */

// Author: mdsteele@google.com (Matthew D. Steele)

#include "net/instaweb/htmlparse/public/html_parse_test_base.h"
#include "net/instaweb/http/public/content_type.h"
#include "net/instaweb/util/public/mock_message_handler.h"
#include "net/instaweb/http/public/response_headers.h"
#include "net/instaweb/rewriter/public/domain_lawyer.h"
#include "net/instaweb/rewriter/public/resource_manager_test_base.h"
#include "net/instaweb/rewriter/public/rewrite_driver.h"
#include "net/instaweb/rewriter/public/rewrite_options.h"
#include "net/instaweb/util/public/basictypes.h"
#include "net/instaweb/util/public/gtest.h"
#include "net/instaweb/util/public/string.h"
#include "net/instaweb/util/public/string_util.h"

namespace net_instaweb {

namespace {

class CssInlineFilterTest : public ResourceManagerTestBase,
                            public ::testing::WithParamInterface<bool> {
 public:
  CssInlineFilterTest() : filters_added_(false) {}

 protected:
  virtual void SetUp() {
    ResourceManagerTestBase::SetUp();
  }

  void TestInlineCssWithOutputUrl(
                     const GoogleString& html_url,
                     const GoogleString& css_url,
                     const GoogleString& css_out_url,
                     const GoogleString& other_attrs,
                     const GoogleString& css_original_body,
                     bool expect_inline,
                     const GoogleString& css_rewritten_body) {
    if (!filters_added_) {
      AddFilter(RewriteOptions::kInlineCss);
      filters_added_ = true;
    }

    GoogleString html_template = StrCat(
        "\n",
        "  <link rel=\"stylesheet\" href=\"%s\"",
        (other_attrs.empty() ? "" : " " + other_attrs) + ">\n",
        "\n",
        "Hello, world!\n");

    const GoogleString html_input =
        StringPrintf(html_template.c_str(), css_url.c_str());

    const GoogleString outline_html_output =
        StringPrintf(html_template.c_str(), css_out_url.c_str());

    // Put original CSS file into our fetcher.
    ResponseHeaders default_css_header;
    SetDefaultLongCacheHeaders(&kContentTypeCss, &default_css_header);
    SetFetchResponse(css_url, default_css_header, css_original_body);

    // Rewrite the HTML page.
    ParseUrl(html_url, html_input);

    const GoogleString expected_output =
        (!expect_inline ? outline_html_output :
         "\n"
         "  \n"
         "\n"
         "Hello, world!\n");
    EXPECT_EQ(AddHtmlBody(expected_output), output_buffer_);
  }

  void TestInlineCss(const GoogleString& html_url,
                     const GoogleString& css_url,
                     const GoogleString& other_attrs,
                     const GoogleString& css_original_body,
                     bool expect_inline,
                     const GoogleString& css_rewritten_body) {
    TestInlineCssWithOutputUrl(
        html_url, css_url, css_url, other_attrs, css_original_body,
        expect_inline, css_rewritten_body);
  }

 private:
  bool filters_added_;
};

TEST_P(CssInlineFilterTest, InlineCssSimple) {
  const GoogleString css = "BODY { color: red; }\n";
  TestInlineCss("http://www.example.com/index.html",
                "http://www.example.com/styles.css",
                "", css, true, css);
}

TEST_P(CssInlineFilterTest, InlineCss404) {
  // Test to make sure that a missing input is handled well.
  SetFetchResponse404("404.css");
  ValidateNoChanges("404", "<link rel=stylesheet href='404.css'>");

  // Second time, to make sure caching doesn't break it.
  ValidateNoChanges("404", "<link rel=stylesheet href='404.css'>");
}

TEST_P(CssInlineFilterTest, InlineCssCached) {
  // Doing it twice should be safe, too.
  const GoogleString css = "BODY { color: red; }\n";
  TestInlineCss("http://www.example.com/index.html",
                "http://www.example.com/styles.css",
                "", css, true, css);
  TestInlineCss("http://www.example.com/index.html",
                "http://www.example.com/styles.css",
                "", css, true, css);
}

TEST_P(CssInlineFilterTest, InlineCssRewriteUrls1) {
  // CSS with a relative URL that needs to be changed:
  const GoogleString css1 =
      "BODY { background-image: url('bg.png'); }\n";
  const GoogleString css2 =
      "BODY { background-image: url('foo/bar/bg.png'); }\n";
  TestInlineCss("http://www.example.com/index.html",
                "http://www.example.com/foo/bar/baz.css",
                "", css1, true, css2);
}

TEST_P(CssInlineFilterTest, InlineCssRewriteUrls2) {
  // CSS with a relative URL, this time with ".." in it:
  const GoogleString css1 =
      "BODY { background-image: url('../quux/bg.png'); }\n";
  const GoogleString css2 =
      "BODY { background-image: url('foo/quux/bg.png'); }\n";
  TestInlineCss("http://www.example.com/index.html",
                "http://www.example.com/foo/bar/baz.css",
                "", css1, true, css2);
}

TEST_P(CssInlineFilterTest, NoRewriteUrlsSameDir) {
  const GoogleString css = "BODY { background-image: url('bg.png'); }\n";
  TestInlineCss("http://www.example.com/index.html",
                "http://www.example.com/baz.css",
                "", css, true, css);
}

TEST_P(CssInlineFilterTest, ShardSubresources) {
  UseMd5Hasher();
  DomainLawyer* lawyer = options()->domain_lawyer();
  lawyer->AddShard("www.example.com", "shard1.com,shard2.com",
                   &message_handler_);

  const GoogleString css_in =
      ".p1 { background-image: url('b1.png'); }"
      ".p2 { background-image: url('b2.png'); }";
  const GoogleString css_out =
      ".p1 { background-image: url('http://shard2.com/b1.png'); }"
      ".p2 { background-image: url('http://shard1.com/b2.png'); }";
  TestInlineCss("http://www.example.com/index.html",
                "http://www.example.com/baz.css",
                "", css_in, true, css_out);
}

TEST_P(CssInlineFilterTest, DoNotInlineCssWithMediaAttr) {
  const GoogleString css = "BODY { color: red; }\n";
  TestInlineCss("http://www.example.com/index.html",
                "http://www.example.com/styles.css",
                "media=\"print\"", css, false, "");
}

TEST_P(CssInlineFilterTest, DoInlineCssWithMediaAll) {
  const GoogleString css = "BODY { color: red; }\n";
  TestInlineCss("http://www.example.com/index.html",
                "http://www.example.com/styles.css",
                "media=\"all\"", css, true, css);
}

TEST_P(CssInlineFilterTest, DoNotInlineCssTooBig) {
  // CSS too large to inline:
  const int64 length = 2 * RewriteOptions::kDefaultCssInlineMaxBytes;
  TestInlineCss("http://www.example.com/index.html",
                "http://www.example.com/styles.css", "",
                ("BODY { background-image: url('" +
                 GoogleString(length, 'z') + ".png'); }\n"),
                false, "");
}

TEST_P(CssInlineFilterTest, DoNotInlineCssDifferentDomain) {
  // Note: This only fails because we haven't autorized www.example.org
  TestInlineCss("http://www.example.com/index.html",
                "http://www.example.org/styles.css",
                "", "BODY { color: red; }\n", false, "");
}

TEST_P(CssInlineFilterTest, CorrectlyInlineCssWithImports) {
  TestInlineCss("http://www.example.com/index.html",
                "http://www.example.com/dir/styles.css", "",
                "@import \"foo.css\"; BODY { color: red; }\n", true,
                "@import \"dir/foo.css\"; BODY { color: red; }\n");
}

// http://code.google.com/p/modpagespeed/issues/detail?q=css&id=252
TEST_P(CssInlineFilterTest, ClaimsXhtmlButHasUnclosedLink) {
  // XHTML text should not have unclosed links.  But if they do, like
  // in Issue 252, then we should leave them alone.
  static const char html_format[] =
      "\n"
      "  %s\n"
      "  %s\n"
      "  "     // 'in' <link>
      "\n"
      "

\n"
      "  Yellow on Blue

";

  static const char unclosed_css[] =
      "  <link rel='stylesheet' href='a.css' type='text/css'>\n";  // unclosed
  static const char inlined_css[] = "  \n";

  // Put original CSS files into our fetcher.
  ResponseHeaders default_css_header;
  SetDefaultLongCacheHeaders(&kContentTypeCss, &default_css_header);
  SetFetchResponse(StrCat(kTestDomain, "a.css"), default_css_header, ".a {}");
  AddFilter(RewriteOptions::kInlineCss);
  ValidateExpected("claims_xhtml_but_has_unclosed_links",
                   StringPrintf(html_format, kXhtmlDtd, unclosed_css),
                   StringPrintf(html_format, kXhtmlDtd, inlined_css));
}

TEST_P(CssInlineFilterTest, InlineCombined) {
  // Make sure we interact with CSS combiner properly, including in cached
  // case.
  options()->EnableFilter(RewriteOptions::kInlineCss);
  options()->EnableFilter(RewriteOptions::kCombineCss);
  rewrite_driver()->AddFilters();

  const char kCssUrl[] = "a.css";
  const char kCss[] = "div {display:block;}";

  InitResponseHeaders(kCssUrl, kContentTypeCss, kCss, 3000);

  GoogleString html_input =
      StrCat("<link rel=stylesheet href=\"", kCssUrl, "\">",
             "<link rel=stylesheet href=\"", kCssUrl, "\">");
  GoogleString html_output= StrCat("");

  ValidateExpected("inline_combined", html_input, html_output);
  ValidateExpected("inline_combined", html_input, html_output);
}

TEST_P(CssInlineFilterTest, InlineMinimizeInteraction) {
  // There was a bug in async mode where we would accidentally prevent
  // minification results from rendering when inlining was not to be done.
  options()->EnableFilter(RewriteOptions::kRewriteCss);
  options()->set_css_inline_max_bytes(4);

  TestInlineCssWithOutputUrl(
      StrCat(kTestDomain, "minimize_but_not_inline.html"),
      StrCat(kTestDomain, "a.css"),
      Encode(kTestDomain, "cf", "0", "a.css", "css"),
      "", /* no other attributes*/
      "div{display: none;}",
      false,
      "div{display: none}");
}

INSTANTIATE_TEST_CASE_P(CssInlineFilterTestInstance,
                        CssInlineFilterTest,
                        ::testing::Bool());

}  // namespace

}  // namespace net_instaweb
<commit_msg>Spelling<commit_after>/*
 * Copyright 2010 Google Inc.
 *
 * Licensed 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.
 */

// Author: mdsteele@google.com (Matthew D. Steele)

#include "net/instaweb/htmlparse/public/html_parse_test_base.h"
#include "net/instaweb/http/public/content_type.h"
#include "net/instaweb/util/public/mock_message_handler.h"
#include "net/instaweb/http/public/response_headers.h"
#include "net/instaweb/rewriter/public/domain_lawyer.h"
#include "net/instaweb/rewriter/public/resource_manager_test_base.h"
#include "net/instaweb/rewriter/public/rewrite_driver.h"
#include "net/instaweb/rewriter/public/rewrite_options.h"
#include "net/instaweb/util/public/basictypes.h"
#include "net/instaweb/util/public/gtest.h"
#include "net/instaweb/util/public/string.h"
#include "net/instaweb/util/public/string_util.h"

namespace net_instaweb {

namespace {

class CssInlineFilterTest : public ResourceManagerTestBase,
                            public ::testing::WithParamInterface<bool> {
 public:
  CssInlineFilterTest() : filters_added_(false) {}

 protected:
  virtual void SetUp() {
    ResourceManagerTestBase::SetUp();
  }

  void TestInlineCssWithOutputUrl(
                     const GoogleString& html_url,
                     const GoogleString& css_url,
                     const GoogleString& css_out_url,
                     const GoogleString& other_attrs,
                     const GoogleString& css_original_body,
                     bool expect_inline,
                     const GoogleString& css_rewritten_body) {
    if (!filters_added_) {
      AddFilter(RewriteOptions::kInlineCss);
      filters_added_ = true;
    }

    GoogleString html_template = StrCat(
        "\n",
        "  <link rel=\"stylesheet\" href=\"%s\"",
        (other_attrs.empty() ? "" : " " + other_attrs) + ">\n",
        "\n",
        "Hello, world!\n");

    const GoogleString html_input =
        StringPrintf(html_template.c_str(), css_url.c_str());

    const GoogleString outline_html_output =
        StringPrintf(html_template.c_str(), css_out_url.c_str());

    // Put original CSS file into our fetcher.
    ResponseHeaders default_css_header;
    SetDefaultLongCacheHeaders(&kContentTypeCss, &default_css_header);
    SetFetchResponse(css_url, default_css_header, css_original_body);

    // Rewrite the HTML page.
    ParseUrl(html_url, html_input);

    const GoogleString expected_output =
        (!expect_inline ? outline_html_output :
         "\n"
         "  \n"
         "\n"
         "Hello, world!\n");
    EXPECT_EQ(AddHtmlBody(expected_output), output_buffer_);
  }

  void TestInlineCss(const GoogleString& html_url,
                     const GoogleString& css_url,
                     const GoogleString& other_attrs,
                     const GoogleString& css_original_body,
                     bool expect_inline,
                     const GoogleString& css_rewritten_body) {
    TestInlineCssWithOutputUrl(
        html_url, css_url, css_url, other_attrs, css_original_body,
        expect_inline, css_rewritten_body);
  }

 private:
  bool filters_added_;
};

TEST_P(CssInlineFilterTest, InlineCssSimple) {
  const GoogleString css = "BODY { color: red; }\n";
  TestInlineCss("http://www.example.com/index.html",
                "http://www.example.com/styles.css",
                "", css, true, css);
}

TEST_P(CssInlineFilterTest, InlineCss404) {
  // Test to make sure that a missing input is handled well.
  SetFetchResponse404("404.css");
  ValidateNoChanges("404", "<link rel=stylesheet href='404.css'>");

  // Second time, to make sure caching doesn't break it.
  ValidateNoChanges("404", "<link rel=stylesheet href='404.css'>");
}

TEST_P(CssInlineFilterTest, InlineCssCached) {
  // Doing it twice should be safe, too.
  const GoogleString css = "BODY { color: red; }\n";
  TestInlineCss("http://www.example.com/index.html",
                "http://www.example.com/styles.css",
                "", css, true, css);
  TestInlineCss("http://www.example.com/index.html",
                "http://www.example.com/styles.css",
                "", css, true, css);
}

TEST_P(CssInlineFilterTest, InlineCssRewriteUrls1) {
  // CSS with a relative URL that needs to be changed:
  const GoogleString css1 =
      "BODY { background-image: url('bg.png'); }\n";
  const GoogleString css2 =
      "BODY { background-image: url('foo/bar/bg.png'); }\n";
  TestInlineCss("http://www.example.com/index.html",
                "http://www.example.com/foo/bar/baz.css",
                "", css1, true, css2);
}

TEST_P(CssInlineFilterTest, InlineCssRewriteUrls2) {
  // CSS with a relative URL, this time with ".." in it:
  const GoogleString css1 =
      "BODY { background-image: url('../quux/bg.png'); }\n";
  const GoogleString css2 =
      "BODY { background-image: url('foo/quux/bg.png'); }\n";
  TestInlineCss("http://www.example.com/index.html",
                "http://www.example.com/foo/bar/baz.css",
                "", css1, true, css2);
}

TEST_P(CssInlineFilterTest, NoRewriteUrlsSameDir) {
  const GoogleString css = "BODY { background-image: url('bg.png'); }\n";
  TestInlineCss("http://www.example.com/index.html",
                "http://www.example.com/baz.css",
                "", css, true, css);
}

TEST_P(CssInlineFilterTest, ShardSubresources) {
  UseMd5Hasher();
  DomainLawyer* lawyer = options()->domain_lawyer();
  lawyer->AddShard("www.example.com", "shard1.com,shard2.com",
                   &message_handler_);

  const GoogleString css_in =
      ".p1 { background-image: url('b1.png'); }"
      ".p2 { background-image: url('b2.png'); }";
  const GoogleString css_out =
      ".p1 { background-image: url('http://shard2.com/b1.png'); }"
      ".p2 { background-image: url('http://shard1.com/b2.png'); }";
  TestInlineCss("http://www.example.com/index.html",
                "http://www.example.com/baz.css",
                "", css_in, true, css_out);
}

TEST_P(CssInlineFilterTest, DoNotInlineCssWithMediaAttr) {
  const GoogleString css = "BODY { color: red; }\n";
  TestInlineCss("http://www.example.com/index.html",
                "http://www.example.com/styles.css",
                "media=\"print\"", css, false, "");
}

TEST_P(CssInlineFilterTest, DoInlineCssWithMediaAll) {
  const GoogleString css = "BODY { color: red; }\n";
  TestInlineCss("http://www.example.com/index.html",
                "http://www.example.com/styles.css",
                "media=\"all\"", css, true, css);
}

TEST_P(CssInlineFilterTest, DoNotInlineCssTooBig) {
  // CSS too large to inline:
  const int64 length = 2 * RewriteOptions::kDefaultCssInlineMaxBytes;
  TestInlineCss("http://www.example.com/index.html",
                "http://www.example.com/styles.css", "",
                ("BODY { background-image: url('" +
                 GoogleString(length, 'z') + ".png'); }\n"),
                false, "");
}

TEST_P(CssInlineFilterTest, DoNotInlineCssDifferentDomain) {
  // Note: This only fails because we haven't authorized www.example.org
  TestInlineCss("http://www.example.com/index.html",
                "http://www.example.org/styles.css",
                "", "BODY { color: red; }\n", false, "");
}

TEST_P(CssInlineFilterTest, CorrectlyInlineCssWithImports) {
  TestInlineCss("http://www.example.com/index.html",
                "http://www.example.com/dir/styles.css", "",
                "@import \"foo.css\"; BODY { color: red; }\n", true,
                "@import \"dir/foo.css\"; BODY { color: red; }\n");
}

// http://code.google.com/p/modpagespeed/issues/detail?q=css&id=252
TEST_P(CssInlineFilterTest, ClaimsXhtmlButHasUnclosedLink) {
  // XHTML text should not have unclosed links.  But if they do, like
  // in Issue 252, then we should leave them alone.
  static const char html_format[] =
      "\n"
      "  %s\n"
      "  %s\n"
      "  "     // 'in' <link>
      "\n"
      "

\n"
      "  Yellow on Blue

";

  static const char unclosed_css[] =
      "  <link rel='stylesheet' href='a.css' type='text/css'>\n";  // unclosed
  static const char inlined_css[] = "  \n";

  // Put original CSS files into our fetcher.
  ResponseHeaders default_css_header;
  SetDefaultLongCacheHeaders(&kContentTypeCss, &default_css_header);
  SetFetchResponse(StrCat(kTestDomain, "a.css"), default_css_header, ".a {}");
  AddFilter(RewriteOptions::kInlineCss);
  ValidateExpected("claims_xhtml_but_has_unclosed_links",
                   StringPrintf(html_format, kXhtmlDtd, unclosed_css),
                   StringPrintf(html_format, kXhtmlDtd, inlined_css));
}

TEST_P(CssInlineFilterTest, InlineCombined) {
  // Make sure we interact with CSS combiner properly, including in cached
  // case.
  options()->EnableFilter(RewriteOptions::kInlineCss);
  options()->EnableFilter(RewriteOptions::kCombineCss);
  rewrite_driver()->AddFilters();

  const char kCssUrl[] = "a.css";
  const char kCss[] = "div {display:block;}";

  InitResponseHeaders(kCssUrl, kContentTypeCss, kCss, 3000);

  GoogleString html_input =
      StrCat("<link rel=stylesheet href=\"", kCssUrl, "\">",
             "<link rel=stylesheet href=\"", kCssUrl, "\">");
  GoogleString html_output= StrCat("");

  ValidateExpected("inline_combined", html_input, html_output);
  ValidateExpected("inline_combined", html_input, html_output);
}

TEST_P(CssInlineFilterTest, InlineMinimizeInteraction) {
  // There was a bug in async mode where we would accidentally prevent
  // minification results from rendering when inlining was not to be done.
  options()->EnableFilter(RewriteOptions::kRewriteCss);
  options()->set_css_inline_max_bytes(4);

  TestInlineCssWithOutputUrl(
      StrCat(kTestDomain, "minimize_but_not_inline.html"),
      StrCat(kTestDomain, "a.css"),
      Encode(kTestDomain, "cf", "0", "a.css", "css"),
      "", /* no other attributes*/
      "div{display: none;}",
      false,
      "div{display: none}");
}

INSTANTIATE_TEST_CASE_P(CssInlineFilterTestInstance,
                        CssInlineFilterTest,
                        ::testing::Bool());

}  // namespace

}  // namespace net_instaweb
<|endoftext|>