<commit_before>#ifndef RPTUI_VIEWSWINDOW_HXX
#define RPTUI_VIEWSWINDOW_HXX
/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: ViewsWindow.hxx,v $
 *
 *  $Revision: 1.4 $
 *
 *  last change: $Author: hr $ $Date: 2007-08-03 10:01:53 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 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
 *
 ************************************************************************/

#ifndef _COM_SUN_STAR_REPORT_XSECTION_HPP_
#include <com/sun/star/report/XSection.hpp>
#endif
#ifndef _SV_WINDOW_HXX
#include <vcl/window.hxx>
#endif
#ifndef INCLUDED_SVTOOLS_COLORCFG_HXX
#include <svtools/colorcfg.hxx>
#endif
#ifndef RPTUI_REPORT_DEFINES_HXX
#include "ReportDefines.hxx"
#endif
#ifndef REPORT_REPORTSECTION_HXX
#include "ReportSection.hxx"
#endif
#ifndef _COMPHELPER_PROPERTY_MULTIPLEX_HXX_
#include <comphelper/propmultiplex.hxx>
#endif
#ifndef _CPPUHELPER_BASEMUTEX_HXX_
#include "cppuhelper/basemutex.hxx"
#endif
#ifndef INCLUDED_SVTOOLS_COLORCFG_HXX
#include <svtools/colorcfg.hxx>
#endif
#ifndef _COM_SUN_STAR_BEANS_NAMEDVALUE_HPP_
#include <com/sun/star/beans/NamedValue.hpp>
#endif
#ifndef _SVDEDTV_HXX
#include <svx/svdedtv.hxx>
#endif
#include <SectionView.hxx>

#include <list>
#include <vector>
#include <boost/shared_ptr.hpp>

#include <MarkedSection.hxx>

class Splitter;
class SdrHdl;
namespace comphelper
{
    class OPropertyChangeMultiplexer;
}
namespace rptui
{
    class OReportWindow;
    class ODesignView;
    class OEndMarker;
    class OReportSection;
    class OSectionView;

    // -----------------------------------------------------------------------------
    struct RectangleLess : public ::std::binary_function< Rectangle, Rectangle, bool>
    {
        enum CompareMode { POS_LEFT,POS_RIGHT,POS_UPPER,POS_DOWN,POS_CENTER_HORIZONTAL,POS_CENTER_VERTICAL };
        CompareMode m_eCompareMode;
        Point       m_aRefPoint;
        RectangleLess(CompareMode _eCompareMode,const Point& _rRefPoint ) : m_eCompareMode(_eCompareMode),m_aRefPoint(_rRefPoint){}
        bool operator() (const Rectangle& lhs, const Rectangle& rhs) const
        {
            switch(m_eCompareMode)
            {
            case POS_LEFT:
                return lhs.Left() < rhs.Left();
            case POS_RIGHT:
                return lhs.Right() >= rhs.Right();
            case POS_UPPER:
                return lhs.Top() < rhs.Top();
            case POS_DOWN:
                return lhs.Bottom() >= rhs.Bottom();
            case POS_CENTER_HORIZONTAL:
                return abs(m_aRefPoint.X() - lhs.Center().X()) < abs(m_aRefPoint.X() - rhs.Center().X());
            case POS_CENTER_VERTICAL:
                return abs(lhs.Center().Y() - m_aRefPoint.Y()) < abs(rhs.Center().Y() - m_aRefPoint.Y());
            }
            return false;
        }
    };

    class OViewsWindow :    public Window
                        ,   public ::cppu::BaseMutex
                        ,   public ::comphelper::OPropertyChangeListener
                        ,   public SfxListener
                        ,   public IMarkedSection
    {
        typedef ::std::multimap<Rectangle,::std::pair<SdrObject*,OSectionView*>,RectangleLess>              TRectangleMap;
    public:
        typedef ::std::pair< ::boost::shared_ptr<OEndMarker>,::boost::shared_ptr<Splitter> >                TSplitterPair;
        typedef ::std::pair< ::boost::shared_ptr<OReportSection> , ::rtl::Reference< comphelper::OPropertyChangeMultiplexer> >
                                                                                TReportPair;
        typedef ::std::pair< TReportPair, TSplitterPair  >                                                  TSectionPair;
        typedef ::std::vector< TSectionPair >                                                               TSectionsMap;

        struct TReportPairHelper : public ::std::unary_function< TSectionsMap::value_type, ::boost::shared_ptr<OReportSection> >
        {
            const ::boost::shared_ptr<OReportSection>& operator() (const TSectionsMap::value_type& lhs) const
            {
                return lhs.first.first;
            }
        };
    private:
        TSectionsMap                            m_aSections;
        svtools::ColorConfig                    m_aColorConfig;
        OReportWindow*                          m_pParent;
        ::rtl::OUString                         m_sShapeType;
        Point                                   m_aPoint;
        sal_Bool                                m_bInSplitHandler;
        sal_Bool                                m_bInUnmark;

        void ImplInitSettings();
        /** returns the iterator at pos _nPos or the end()
        */
        TSectionsMap::iterator getIteratorAtPos(USHORT _nPos);
        void collectRectangles(TRectangleMap& _rMap,bool _bBoundRects);
        void collectBoundResizeRect(const TRectangleMap& _rSortRectangles,sal_Int32 _nControlModification,bool _bAlignAtSection,bool _bBoundRects,Rectangle& _rBound,Rectangle& _rResize);

        DECL_LINK(StartSplitHdl, Splitter*);
        DECL_LINK(SplitHdl, Splitter*);
        DECL_LINK(EndSplitHdl, Splitter*);

        OViewsWindow(OViewsWindow&);
        void operator =(OViewsWindow&);
    protected:
        virtual void DataChanged( const DataChangedEvent& rDCEvt );
        // windows overload
        virtual void MouseButtonDown( const MouseEvent& rMEvt );
        virtual void MouseButtonUp( const MouseEvent& rMEvt );
        using Window::Notify;
        // SfxListener
        virtual void Notify(SfxBroadcaster & rBc, SfxHint const & rHint);
        // OPropertyChangeListener
        virtual void    _propertyChanged(const ::com::sun::star::beans::PropertyChangeEvent& _rEvent) throw( ::com::sun::star::uno::RuntimeException);
    public:
        OViewsWindow( Window* _pParent,OReportWindow* _pReportWindow);
        virtual ~OViewsWindow();

        // windows overload
        virtual void Resize();

        /** late ctor
        */
        void initialize();

        inline OReportWindow*       getView()           const { return m_pParent; }

        /** removes the section at the given position.
        *
        * \param _nPosition Zero based.
        */
        void            removeSection(USHORT _nPosition);

        /** adds a new section at position _nPosition.
            If the section is <NULL/> nothing happens.
            If the position is grater than the current elements, the section will be appended.
        */
        void            addSection(const ::com::sun::star::uno::Reference< ::com::sun::star::report::XSection >& _xSection
                                    ,const ::rtl::OUString& _sColorEntry
                                    ,USHORT _nPosition = USHRT_MAX);

        USHORT          getSectionCount() const;
        /** return the section at the given position
        *
        * \param _nPos
        * \return the section at this pos or an empty section
        */
        ::boost::shared_ptr<OReportSection> getSection(const USHORT _nPos) const;

        void            showView(USHORT _nPos,BOOL _bShow);

        /** turns the grid on or off
        *
        * \param _bVisible
        */
        void            toggleGrid(sal_Bool _bVisible);
        void            setGridSnap(BOOL bOn);
        void            setDragStripes(BOOL bOn);
        BOOL            isDragStripes() const;

        /** returns the total accumulated height of all sections until _pSection is reached
            @param  _pSection Defines the end of accumulation, can be <NULL/>
        */
        sal_Int32       getTotalHeight(const OReportSection* _pSection = NULL) const;

        /** returns the height of the first spliiter.
        */
        sal_Int32       getSplitterHeight() const;

        inline bool     empty() const { return m_aSections.empty(); }
        void            SetMode( DlgEdMode m_eMode );
        void            SetInsertObj( USHORT eObj,const ::rtl::OUString& _sShapeType = ::rtl::OUString());
        rtl::OUString   GetInsertObjString() const;
        /** copies the current selection in this section
        */
        void Copy();

        /** returns if paste is allowed
        *
        * \return <TRUE/> if paste is allowed
        */
        BOOL IsPasteAllowed();

        /** paste a new control in this section
        */
        void Paste();

        /** Deletes the current selection in this section
        *
        */
        void Delete();

        /** All objects will be marked.
        */
        void SelectAll();

        /** returns <TRUE/> when a object is marked
        */
        BOOL HasSelection();

        void            SectionHasFocus(OReportSection* _pSection,BOOL _bHasFocus);

        /** unmark all objects on the views without the given one.
        *
        * @param _pSectionView The view where the objects should not be unmarked.
        */
        void            unmarkAllObjects(OSectionView* _pSectionView);

        /** returns the report section window for the given xsection
            @param  _xSection   the section
        */
        ::boost::shared_ptr<OReportSection> getReportSection(const ::com::sun::star::uno::Reference< ::com::sun::star::report::XSection >& _xSection);

        /** checks if the keycode is known by the child windows
            @param  _rCode  the keycode
            @return <TRUE/> if the keycode is handled otherwise <FALSE/>
        */
        sal_Bool        handleKeyEvent(const KeyEvent& _rEvent);

        /** the the section as marked or not marked
            @param  _pSectionView   the section where to set the marked flag
            @param  _bMark  the marked flag
        */
        void            setMarked(OSectionView* _pSectionView,sal_Bool _bMark);
        void            setMarked(const ::com::sun::star::uno::Reference< ::com::sun::star::report::XSection>& _xSection,sal_Bool _bMark);
        void            setMarked(const ::com::sun::star::uno::Sequence< ::com::sun::star::uno::Reference< ::com::sun::star::report::XReportComponent> >& _xShape,sal_Bool _bMark);

        /** get section from point inside.
            @param  _aPosition
        */
        ::boost::shared_ptr<OReportSection> getSection(const Point& _aPosition);

        // IMarkedSection
        ::boost::shared_ptr<OReportSection> getMarkedSection(NearSectionAccess nsa = CURRENT) const;
        virtual void markSection(const sal_uInt16 _nPos);

        /** align all marked objects in all sections
        */
        void alignMarkedObjects(sal_Int32 _nControlModification,bool _bAlignAtSection, bool bBoundRects = false);

        /** shows the properties of the section corresponding to the end marker
        *
        * \param _pEndMarker must be not <NULL/>
        */
        void showProperties(const OEndMarker* _pEndMarker);

        /** creates a default object
        *
        */
        void createDefault();

        /** returns the currently set shape type.
        *
        * \return \member m_sShapeType
        */
        inline ::rtl::OUString getShapeType() const { return m_sShapeType; }

        /** returns the current position in the list
        */
        USHORT getPosition(const OReportSection* _pSection = NULL) const;

        /** calls on every section BrkAction
        *
        */
        void BrkAction();
        void BegMarkObj(const Point& _aPnt,const OSectionView* _pSection);
        void BegDragObj(const Point& _aPnt, SdrHdl* _pHdl,const OSectionView* _pSection);
        void EndDragObj(BOOL _bDragIntoNewSection,const OSectionView* _pSection,const Point& _aPnt);
        void EndAction();
        void ForceMarkedToAnotherPage();
        BOOL IsAction() const;
        BOOL IsDragObj() const;
        void handleKey(const KeyCode& _rCode);
        void stopScrollTimer();

        /** return the section at the given point which is relative to the given section
        *
        * \param _pSection the section which is used as reference point
        * \param _rPnt the point, it will be changed that it is inside the section which will be returned
        * \return the section
        */
        OSectionView* getSectionRelativeToPosition(const OSectionView* _pSection,Point& _rPnt);

        void MovAction(const Point& rPnt,const OSectionView* _pSection,bool _bMove = true);
        void setPoint(const Point& _aPnt);
        inline Point getPoint() const { return m_aPoint; }

        sal_uInt32 getMarkedObjectCount() const;
    };
//==============================================================================
} // rptui
//==============================================================================
#endif // RPTUI_VIEWSWINDOW_HXX

<commit_msg>INTEGRATION: CWS reportdesign02 (1.4.36); FILE MERGED 2007/11/28 15:19:29 lla 1.4.36.2: #i80260# with pressed ctrl key it isn't possible to move the object out of the section. 2007/11/26 06:24:44 lla 1.4.36.1: #i80260# drag n drop, lines over all sections, no ctrl key need to move obj in different section<commit_after>#ifndef RPTUI_VIEWSWINDOW_HXX
#define RPTUI_VIEWSWINDOW_HXX
/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: ViewsWindow.hxx,v $
 *
 *  $Revision: 1.5 $
 *
 *  last change: $Author: rt $ $Date: 2008-01-29 13:49:05 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 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
 *
 ************************************************************************/

#ifndef _COM_SUN_STAR_REPORT_XSECTION_HPP_
#include <com/sun/star/report/XSection.hpp>
#endif
#ifndef _SV_WINDOW_HXX
#include <vcl/window.hxx>
#endif
#ifndef INCLUDED_SVTOOLS_COLORCFG_HXX
#include <svtools/colorcfg.hxx>
#endif
#ifndef RPTUI_REPORT_DEFINES_HXX
#include "ReportDefines.hxx"
#endif
#ifndef REPORT_REPORTSECTION_HXX
#include "ReportSection.hxx"
#endif
#ifndef _COMPHELPER_PROPERTY_MULTIPLEX_HXX_
#include <comphelper/propmultiplex.hxx>
#endif
#ifndef _CPPUHELPER_BASEMUTEX_HXX_
#include "cppuhelper/basemutex.hxx"
#endif
#ifndef INCLUDED_SVTOOLS_COLORCFG_HXX
#include <svtools/colorcfg.hxx>
#endif
#ifndef _COM_SUN_STAR_BEANS_NAMEDVALUE_HPP_
#include <com/sun/star/beans/NamedValue.hpp>
#endif
#ifndef _SVDEDTV_HXX
#include <svx/svdedtv.hxx>
#endif
#include <SectionView.hxx>

#include <list>
#include <vector>
#include <boost/shared_ptr.hpp>

#include <MarkedSection.hxx>

class Splitter;
class SdrHdl;
namespace comphelper
{
    class OPropertyChangeMultiplexer;
}
namespace rptui
{
    class OReportWindow;
    class ODesignView;
    class OEndMarker;
    class OReportSection;
    class OSectionView;

    // -----------------------------------------------------------------------------
    struct RectangleLess : public ::std::binary_function< Rectangle, Rectangle, bool>
    {
        enum CompareMode { POS_LEFT,POS_RIGHT,POS_UPPER,POS_DOWN,POS_CENTER_HORIZONTAL,POS_CENTER_VERTICAL };
        CompareMode m_eCompareMode;
        Point       m_aRefPoint;
        RectangleLess(CompareMode _eCompareMode,const Point& _rRefPoint ) : m_eCompareMode(_eCompareMode),m_aRefPoint(_rRefPoint){}
        bool operator() (const Rectangle& lhs, const Rectangle& rhs) const
        {
            switch(m_eCompareMode)
            {
            case POS_LEFT:
                return lhs.Left() < rhs.Left();
            case POS_RIGHT:
                return lhs.Right() >= rhs.Right();
            case POS_UPPER:
                return lhs.Top() < rhs.Top();
            case POS_DOWN:
                return lhs.Bottom() >= rhs.Bottom();
            case POS_CENTER_HORIZONTAL:
                return abs(m_aRefPoint.X() - lhs.Center().X()) < abs(m_aRefPoint.X() - rhs.Center().X());
            case POS_CENTER_VERTICAL:
                return abs(lhs.Center().Y() - m_aRefPoint.Y()) < abs(rhs.Center().Y() - m_aRefPoint.Y());
            }
            return false;
        }
    };

    class OViewsWindow :    public Window
                        ,   public ::cppu::BaseMutex
                        ,   public ::comphelper::OPropertyChangeListener
                        ,   public SfxListener
                        ,   public IMarkedSection
    {
        typedef ::std::multimap<Rectangle,::std::pair<SdrObject*,OSectionView*>,RectangleLess>              TRectangleMap;
    public:
        typedef ::std::pair< ::boost::shared_ptr<OEndMarker>,::boost::shared_ptr<Splitter> >                TSplitterPair;
        typedef ::std::pair< ::boost::shared_ptr<OReportSection> , ::rtl::Reference< comphelper::OPropertyChangeMultiplexer> >
                                                                                TReportPair;
        typedef ::std::pair< TReportPair, TSplitterPair  >                                                  TSectionPair;
        typedef ::std::vector< TSectionPair >                                                               TSectionsMap;

        struct TReportPairHelper : public ::std::unary_function< TSectionsMap::value_type, ::boost::shared_ptr<OReportSection> >
        {
            const ::boost::shared_ptr<OReportSection>& operator() (const TSectionsMap::value_type& lhs) const
            {
                return lhs.first.first;
            }
        };
    private:
        TSectionsMap                            m_aSections;
        svtools::ColorConfig                    m_aColorConfig;
        OReportWindow*                          m_pParent;
        ::rtl::OUString                         m_sShapeType;
        Point                                   m_aPoint;
        sal_Bool                                m_bInSplitHandler;
        sal_Bool                                m_bInUnmark;

        void ImplInitSettings();
        /** returns the iterator at pos _nPos or the end()
        */
        TSectionsMap::iterator getIteratorAtPos(USHORT _nPos);
        void collectRectangles(TRectangleMap& _rMap,bool _bBoundRects);
        void collectBoundResizeRect(const TRectangleMap& _rSortRectangles,sal_Int32 _nControlModification,bool _bAlignAtSection,bool _bBoundRects,Rectangle& _rBound,Rectangle& _rResize);

        DECL_LINK(StartSplitHdl, Splitter*);
        DECL_LINK(SplitHdl, Splitter*);
        DECL_LINK(EndSplitHdl, Splitter*);

        OViewsWindow(OViewsWindow&);
        void operator =(OViewsWindow&);
    protected:
        virtual void DataChanged( const DataChangedEvent& rDCEvt );
        // windows overload
        virtual void MouseButtonDown( const MouseEvent& rMEvt );
        virtual void MouseButtonUp( const MouseEvent& rMEvt );
        using Window::Notify;
        // SfxListener
        virtual void Notify(SfxBroadcaster & rBc, SfxHint const & rHint);
        // OPropertyChangeListener
        virtual void    _propertyChanged(const ::com::sun::star::beans::PropertyChangeEvent& _rEvent) throw( ::com::sun::star::uno::RuntimeException);
    public:
        OViewsWindow( Window* _pParent,OReportWindow* _pReportWindow);
        virtual ~OViewsWindow();

        // windows overload
        virtual void Resize();

        /** late ctor
        */
        void initialize();

        inline OReportWindow*       getView()           const { return m_pParent; }

        /** removes the section at the given position.
        *
        * \param _nPosition Zero based.
        */
        void            removeSection(USHORT _nPosition);

        /** adds a new section at position _nPosition.
            If the section is <NULL/> nothing happens.
            If the position is grater than the current elements, the section will be appended.
        */
        void            addSection(const ::com::sun::star::uno::Reference< ::com::sun::star::report::XSection >& _xSection
                                    ,const ::rtl::OUString& _sColorEntry
                                    ,USHORT _nPosition = USHRT_MAX);

        USHORT          getSectionCount() const;
        /** return the section at the given position
        *
        * \param _nPos
        * \return the section at this pos or an empty section
        */
        ::boost::shared_ptr<OReportSection> getSection(const USHORT _nPos) const;

        void            showView(USHORT _nPos,BOOL _bShow);

        /** turns the grid on or off
        *
        * \param _bVisible
        */
        void            toggleGrid(sal_Bool _bVisible);
        void            setGridSnap(BOOL bOn);
        void            setDragStripes(BOOL bOn);
        BOOL            isDragStripes() const;

        /** returns the total accumulated height of all sections until _pSection is reached
            @param  _pSection Defines the end of accumulation, can be <NULL/>
        */
        sal_Int32       getTotalHeight(const OReportSection* _pSection = NULL) const;

        /** returns the height of the first spliiter.
        */
        sal_Int32       getSplitterHeight() const;

        inline bool     empty() const { return m_aSections.empty(); }
        void            SetMode( DlgEdMode m_eMode );
        void            SetInsertObj( USHORT eObj,const ::rtl::OUString& _sShapeType = ::rtl::OUString());
        rtl::OUString   GetInsertObjString() const;
        /** copies the current selection in this section
        */
        void Copy();

        /** returns if paste is allowed
        *
        * \return <TRUE/> if paste is allowed
        */
        BOOL IsPasteAllowed();

        /** paste a new control in this section
        */
        void Paste();

        /** Deletes the current selection in this section
        *
        */
        void Delete();

        /** All objects will be marked.
        */
        void SelectAll();

        /** returns <TRUE/> when a object is marked
        */
        BOOL HasSelection();

        void            SectionHasFocus(OReportSection* _pSection,BOOL _bHasFocus);

        /** unmark all objects on the views without the given one.
        *
        * @param _pSectionView The view where the objects should not be unmarked.
        */
        void            unmarkAllObjects(OSectionView* _pSectionView);

        /** returns the report section window for the given xsection
            @param  _xSection   the section
        */
        ::boost::shared_ptr<OReportSection> getReportSection(const ::com::sun::star::uno::Reference< ::com::sun::star::report::XSection >& _xSection);

        /** checks if the keycode is known by the child windows
            @param  _rCode  the keycode
            @return <TRUE/> if the keycode is handled otherwise <FALSE/>
        */
        sal_Bool        handleKeyEvent(const KeyEvent& _rEvent);

        /** the the section as marked or not marked
            @param  _pSectionView   the section where to set the marked flag
            @param  _bMark  the marked flag
        */
        void            setMarked(OSectionView* _pSectionView,sal_Bool _bMark);
        void            setMarked(const ::com::sun::star::uno::Reference< ::com::sun::star::report::XSection>& _xSection,sal_Bool _bMark);
        void            setMarked(const ::com::sun::star::uno::Sequence< ::com::sun::star::uno::Reference< ::com::sun::star::report::XReportComponent> >& _xShape,sal_Bool _bMark);

        /** get section from point inside.
            @param  _aPosition
        */
        ::boost::shared_ptr<OReportSection> getSection(const Point& _aPosition);

        // IMarkedSection
        ::boost::shared_ptr<OReportSection> getMarkedSection(NearSectionAccess nsa = CURRENT) const;
        virtual void markSection(const sal_uInt16 _nPos);

        /** align all marked objects in all sections
        */
        void alignMarkedObjects(sal_Int32 _nControlModification,bool _bAlignAtSection, bool bBoundRects = false);

        /** shows the properties of the section corresponding to the end marker
        *
        * \param _pEndMarker must be not <NULL/>
        */
        void showProperties(const OEndMarker* _pEndMarker);

        /** creates a default object
        *
        */
        void createDefault();

        /** returns the currently set shape type.
        *
        * \return \member m_sShapeType
        */
        inline ::rtl::OUString getShapeType() const { return m_sShapeType; }

        /** returns the current position in the list
        */
        USHORT getPosition(const OReportSection* _pSection = NULL) const;

        /** calls on every section BrkAction
        *
        */
        void BrkAction();
        void BegMarkObj(const Point& _aPnt,const OSectionView* _pSection);
    private:
        void BegDragObj_createInvisibleObjectAtPosition(const Rectangle& _aRect, const OSectionView* _pSection);
        void EndDragObj_removeInvisibleObjects();
        Point m_aDragDelta;
        ::std::vector<SdrObject*> m_aBegDragTempList;
        bool isObjectInMyTempList(SdrObject *);
    public:
        void BegDragObj(const Point& _aPnt, SdrHdl* _pHdl,const OSectionView* _pSection);
        void EndDragObj(BOOL _bDragIntoNewSection,const OSectionView* _pSection,const Point& _aPnt);

        void EndAction();
        void ForceMarkedToAnotherPage();
        BOOL IsAction() const;
        BOOL IsDragObj() const;
        void handleKey(const KeyCode& _rCode);
        void stopScrollTimer();

        /** return the section at the given point which is relative to the given section
        *
        * \param _pSection the section which is used as reference point
        * \param _rPnt the point, it will be changed that it is inside the section which will be returned
        * \return the section
        */
        OSectionView* getSectionRelativeToPosition(const OSectionView* _pSection,Point& _rPnt);

        void MovAction(const Point& rPnt,const OSectionView* _pSection,bool _bMove /*= true */, bool _bControlKeySet);
        // void MovAction2(const Point& rPnt,const OSectionView* _pSection);
        void setPoint(const Point& _aPnt);
        inline Point getPoint() const { return m_aPoint; }

        sal_uInt32 getMarkedObjectCount() const;
    };
//==============================================================================
} // rptui
//==============================================================================
#endif // RPTUI_VIEWSWINDOW_HXX

<|endoftext|>


<commit_before>////////////////////////////////////////////////////////////////////////////////
/// DISCLAIMER
///
/// Copyright 2014-2016 ArangoDB GmbH, Cologne, Germany
/// Copyright 2004-2014 triAGENS GmbH, Cologne, Germany
///
/// 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.
///
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
///
/// @author Kaveh Vahedipour
////////////////////////////////////////////////////////////////////////////////

#include "Inception.h"

#include "Agency/Agent.h"
#include "Agency/GossipCallback.h"
#include "Agency/MeasureCallback.h"
#include "Basics/ConditionLocker.h"
#include "Cluster/ClusterComm.h"

#include <chrono>
#include <numeric>
#include <thread>

using namespace arangodb::consensus;

Inception::Inception() : Thread("Inception"), _agent(nullptr) {}

Inception::Inception(Agent* agent) : Thread("Inception"), _agent(agent) {}

// Shutdown if not already
Inception::~Inception() { shutdown(); }

/// Gossip to others
/// - Get snapshot of gossip peers and agent pool
/// - Create outgoing gossip.
/// - Send to all peers
void Inception::gossip() {

  auto s = std::chrono::system_clock::now();
  std::chrono::seconds timeout(120);
  size_t i = 0;

  CONDITION_LOCKER(guard, _cv);

  while (!this->isStopping() && !_agent->isStopping()) {

    config_t config = _agent->config();  // get a copy of conf

    // Build gossip message
    query_t out = std::make_shared<Builder>();
    out->openObject();
    out->add("endpoint", VPackValue(config.endpoint()));
    out->add("id", VPackValue(config.id()));
    out->add("pool", VPackValue(VPackValueType::Object));
    for (auto const& i : config.pool()) {
      out->add(i.first, VPackValue(i.second));
    }
    out->close();
    out->close();

    std::string path = privApiPrefix + "gossip";

    // gossip peers
    for (auto const& p : config.gossipPeers()) {
      if (p != config.endpoint()) {
        std::string clientid = config.id() + std::to_string(i++);
        auto hf =
            std::make_unique<std::unordered_map<std::string, std::string>>();
        arangodb::ClusterComm::instance()->asyncRequest(
          clientid, 1, p, rest::RequestType::POST, path,
          std::make_shared<std::string>(out->toJson()), hf,
          std::make_shared<GossipCallback>(_agent), 1.0, true, 0.5);
      }
    }

    // pool entries
    for (auto const& pair : config.pool()) {
      if (pair.second != config.endpoint()) {
        std::string clientid = config.id() + std::to_string(i++);
        auto hf =
            std::make_unique<std::unordered_map<std::string, std::string>>();
        arangodb::ClusterComm::instance()->asyncRequest(
          clientid, 1, pair.second, rest::RequestType::POST, path,
          std::make_shared<std::string>(out->toJson()), hf,
          std::make_shared<GossipCallback>(_agent), 1.0, true, 0.5);
      }
    }

    // don't panic
    _cv.wait(100000);

    // Timed out? :(
    if ((std::chrono::system_clock::now() - s) > timeout) {
      if (config.poolComplete()) {
        LOG_TOPIC(DEBUG, Logger::AGENCY) << "Stopping active gossipping!";
      } else {
        LOG_TOPIC(ERR, Logger::AGENCY)
          << "Failed to find complete pool of agents. Giving up!";
      }
      break;
    }

    // We're done
    if (config.poolComplete()) {
      _agent->startConstituent();
      break;
    }

  }

}

// @brief Active agency from persisted database
bool Inception::activeAgencyFromPersistence() {

  auto myConfig = _agent->config();
  std::string const path = pubApiPrefix + "config";

  // Can only be done responcibly, if we are complete
  if (myConfig.poolComplete()) {

    // Contact hosts on pool in hopes of finding a leader Id
    for (auto const& pair : myConfig.pool()) {

      if (pair.first != myConfig.id()) {

        auto comres = arangodb::ClusterComm::instance()->syncRequest(
          myConfig.id(), 1, pair.second, rest::RequestType::GET, path,
          std::string(), std::unordered_map<std::string, std::string>(), 1.0);

        if (comres->status == CL_COMM_SENT) {

          auto body = comres->result->getBodyVelocyPack();
          auto theirConfig = body->slice();

          std::string leaderId;

          // LeaderId in configuration?
          try {
            leaderId = theirConfig.get("leaderId").copyString();
          } catch (std::exception const& e) {
            LOG_TOPIC(DEBUG, Logger::AGENCY)
              << "Failed to get leaderId from" << pair.second << ": "
              << e.what();
          }

          if (leaderId != "") { // Got leaderId. Let's get do it.

            try {
              LOG_TOPIC(DEBUG, Logger::AGENCY)
                << "Found active agency with leader " << leaderId
                << " at endpoint "
                << theirConfig.get("configuration").get(
                  "pool").get(leaderId).copyString();
            } catch (std::exception const& e) {
              LOG_TOPIC(DEBUG, Logger::AGENCY)
                << "Failed to get leaderId from" << pair.second << ": "
                << e.what();
            }

            auto agency = std::make_shared<Builder>();
            agency->openObject();
            agency->add("term", theirConfig.get("term"));
            agency->add("id", VPackValue(leaderId));
            agency->add("active", theirConfig.get("configuration").get("active"));
            agency->add("pool", theirConfig.get("configuration").get("pool"));
            agency->close();
            _agent->notify(agency);

            return true;

          } else { // No leaderId. Move on.

            LOG_TOPIC(DEBUG, Logger::AGENCY)
              << "Failed to get leaderId from" << pair.second;

          }

        }

      }

    }
  }

  return false;

}

bool Inception::restartingActiveAgent() {

  auto myConfig = _agent->config();
  std::string const path = pubApiPrefix + "config";

  auto s = std::chrono::system_clock::now();
  std::chrono::seconds timeout(60);

  // Can only be done responcibly, if we are complete
  if (myConfig.poolComplete()) {

    auto pool = myConfig.pool();
    auto active = myConfig.active();

    CONDITION_LOCKER(guard, _cv);

    while (!this->isStopping() && !_agent->isStopping()) {

      active.erase(
        std::remove(active.begin(), active.end(), myConfig.id()), active.end());
      active.erase(
        std::remove(active.begin(), active.end(), ""), active.end());

      if (active.empty()) {
        return true;
      }

      for (auto& i : active) {

        if (i != myConfig.id() && i != "") {

          auto clientId = myConfig.id();
          auto comres   = arangodb::ClusterComm::instance()->syncRequest(
            clientId, 1, pool.at(i), rest::RequestType::GET, path, std::string(),
            std::unordered_map<std::string, std::string>(), 2.0);

          if (comres->status == CL_COMM_SENT) {

            try {

              auto theirActive = comres->result->getBodyVelocyPack()->
                slice().get("configuration").get("active").toJson();
              auto myActive = myConfig.activeToBuilder()->toJson();

              if (theirActive != myActive) {
                LOG_TOPIC(FATAL, Logger::AGENCY)
                  << "Assumed active RAFT peer and I disagree on active membership."
                  << "Administrative intervention needed.";
                FATAL_ERROR_EXIT();
                return false;
              } else {
                i = "";
              }

            } catch (std::exception const& e) {
              LOG_TOPIC(FATAL, Logger::AGENCY)
                << "Assumed active RAFT peer has no active agency list: " << e.what()
                << "Administrative intervention needed.";
              FATAL_ERROR_EXIT();
              return false;
            }
          }
        }

      }

      // Timed out? :(
      if ((std::chrono::system_clock::now() - s) > timeout) {
        if (myConfig.poolComplete()) {
          LOG_TOPIC(DEBUG, Logger::AGENCY) << "Stopping active gossipping!";
        } else {
          LOG_TOPIC(ERR, Logger::AGENCY)
            << "Failed to find complete pool of agents. Giving up!";
        }
        break;
      }

      _cv.wait(500000);

    }
  }

  return false;

}

inline static int64_t timeStamp() {
  using namespace std::chrono;
  return duration_cast<microseconds>(
    steady_clock::now().time_since_epoch()).count();
}

void Inception::reportIn(std::string const& peerId, uint64_t start) {
  MUTEX_LOCKER(lock, _pLock);
  _pings.push_back(1.0e-3*(double)(timeStamp()-start));
}

void Inception::reportIn(query_t const& query) {

  VPackSlice slice = query->slice();

  TRI_ASSERT(slice.isObject());
  TRI_ASSERT(slice.hasKey("mean"));
  TRI_ASSERT(slice.hasKey("stdev"));
  TRI_ASSERT(slice.hasKey("min"));
  TRI_ASSERT(slice.hasKey("max"));

  MUTEX_LOCKER(lock, _mLock);
  _measurements.push_back(
    std::vector<double>(
      {slice.get("mean").getDouble(), slice.get("stdev").getDouble(),
          slice.get("max").getDouble(), slice.get("min").getDouble()} ));

}

bool Inception::estimateRAFTInterval() {

  using namespace std::chrono;

  std::string path("/_api/agency/config");
  auto pool = _agent->config().pool();
  auto myid = _agent->id();

  for (size_t i = 0; i < 25; ++i) {
    for (auto const& peer : pool) {
      if (peer.first != myid) {
        std::string clientid = peer.first + std::to_string(i);
        auto hf =
          std::make_unique<std::unordered_map<std::string, std::string>>();
        arangodb::ClusterComm::instance()->asyncRequest(
          clientid, 1, peer.second, rest::RequestType::GET, path,
          std::make_shared<std::string>(), hf,
          std::make_shared<MeasureCallback>(this, peer.second, timeStamp()),
          2.0, true);
      }
    }
    std::this_thread::sleep_for(std::chrono::duration<double,std::milli>(5));
  }

  auto s = system_clock::now();
  seconds timeout(3);

  CONDITION_LOCKER(guard, _cv);

  while (true) {

    _cv.wait(50000);

    {
      MUTEX_LOCKER(lock, _pLock);
      if (_pings.size() == 25*(pool.size()-1)) {
        LOG_TOPIC(DEBUG, Logger::AGENCY) << "All pings are in";
        break;
      }
    }

    if ((system_clock::now() - s) > timeout) {
      LOG_TOPIC(DEBUG, Logger::AGENCY) << "Timed out waiting for pings";
      break;
    }

  }

  double sum, mean = 0., sq_sum, stdev = 0., mx = 0., mn = 0.;

  try {

    MUTEX_LOCKER(lock, _pLock);
    size_t num = _pings.size();
    sum    = std::accumulate(_pings.begin(), _pings.end(), 0.0);
    mean   = sum / num;
    mx     = *std::max_element(_pings.begin(), _pings.end());
    mn     = *std::min_element(_pings.begin(), _pings.end());
    std::transform(_pings.begin(), _pings.end(), _pings.begin(),
                   std::bind2nd(std::minus<double>(), mean));
    sq_sum =
      std::inner_product(_pings.begin(), _pings.end(), _pings.begin(), 0.0);
    stdev = std::sqrt(sq_sum / num);

    LOG_TOPIC(DEBUG, Logger::AGENCY)
      << "mean(" << mean << ") stdev(" << stdev<< ")";

  } catch (std::exception const& e) {
    LOG_TOPIC(WARN, Logger::AGENCY) << e.what();
  }

  Builder measurement;
  measurement.openObject();
  measurement.add("mean", VPackValue(mean));
  measurement.add("stdev", VPackValue(stdev));
  measurement.add("min", VPackValue(mn));
  measurement.add("max", VPackValue(mx));
  measurement.close();
  std::string measjson = measurement.toJson();

  path = privApiPrefix + "measure";
  for (auto const& peer : pool) {
    if (peer.first != myid) {
      auto clientId = "1";
      auto comres   = arangodb::ClusterComm::instance()->syncRequest(
        clientId, 1, peer.second, rest::RequestType::POST, path,
        measjson, std::unordered_map<std::string, std::string>(), 2.0);
    }
  }

  {
    MUTEX_LOCKER(lock, _mLock);
    _measurements.push_back(std::vector<double>({mean, stdev, mx, mn}));
  }
  s = system_clock::now();
  while (true) {

    _cv.wait(50000);

    {
      MUTEX_LOCKER(lock, _mLock);
      if (_measurements.size() == pool.size()) {
        LOG_TOPIC(DEBUG, Logger::AGENCY) << "All measurements are in";
        break;
      }
    }

    if ((system_clock::now() - s) > timeout) {
      LOG_TOPIC(WARN, Logger::AGENCY)
        << "Timed out waiting for other measurements. Auto-adaptation failed! Will stick to command line arguments";
      return false;
    }

  }

  double maxmean  = .0;
  double maxstdev = .0;
  for (auto const& meas : _measurements) {
    if (maxmean < meas[3]) {
      maxmean = meas[3];
    }
    if (maxstdev < meas[1]) {
      maxstdev = meas[1];
    }
  }

  maxmean = 1.0e-2*std::ceil(100*(.15 + 1.0e-3*maxmean));

  LOG_TOPIC(INFO, Logger::AGENCY)
    << "Auto-adapting RAFT timing to: {" << maxmean
    << ", " << 5.0*maxmean << "}s";

  _agent->resetRAFTTimes(maxmean, 5.0*maxmean);

  return true;

}

// @brief Active agency from persisted database
bool Inception::activeAgencyFromCommandLine() {
  return false;
}

// @brief Thread main
void Inception::run() {

  // 1. If active agency, do as you're told
  if (activeAgencyFromPersistence()) {
    _agent->ready(true);
  }

  // 2. If we think that we used to be active agent
  if (!_agent->ready() && restartingActiveAgent()) {
    _agent->ready(true);
  }

  // 3. Else gossip
  config_t config = _agent->config();
  if (!_agent->ready() && !config.poolComplete()) {
    gossip();
  }

  // 4. If still incomplete bail out :(
  config = _agent->config();
  if (!_agent->ready() && !config.poolComplete()) {
    LOG_TOPIC(FATAL, Logger::AGENCY)
      << "Failed to build environment for RAFT algorithm. Bailing out!";
    FATAL_ERROR_EXIT();
  }

  estimateRAFTInterval();

  _agent->ready(true);

}

// @brief Graceful shutdown
void Inception::beginShutdown() {
  Thread::beginShutdown();
  CONDITION_LOCKER(guard, _cv);
  guard.broadcast();
}
<commit_msg>RAFT timings to mean rather than max<commit_after>////////////////////////////////////////////////////////////////////////////////
/// DISCLAIMER
///
/// Copyright 2014-2016 ArangoDB GmbH, Cologne, Germany
/// Copyright 2004-2014 triAGENS GmbH, Cologne, Germany
///
/// 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.
///
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
///
/// @author Kaveh Vahedipour
////////////////////////////////////////////////////////////////////////////////

#include "Inception.h"

#include "Agency/Agent.h"
#include "Agency/GossipCallback.h"
#include "Agency/MeasureCallback.h"
#include "Basics/ConditionLocker.h"
#include "Cluster/ClusterComm.h"

#include <chrono>
#include <numeric>
#include <thread>

using namespace arangodb::consensus;

Inception::Inception() : Thread("Inception"), _agent(nullptr) {}

Inception::Inception(Agent* agent) : Thread("Inception"), _agent(agent) {}

// Shutdown if not already
Inception::~Inception() { shutdown(); }

/// Gossip to others
/// - Get snapshot of gossip peers and agent pool
/// - Create outgoing gossip.
/// - Send to all peers
void Inception::gossip() {

  auto s = std::chrono::system_clock::now();
  std::chrono::seconds timeout(120);
  size_t i = 0;

  CONDITION_LOCKER(guard, _cv);

  while (!this->isStopping() && !_agent->isStopping()) {

    config_t config = _agent->config();  // get a copy of conf

    // Build gossip message
    query_t out = std::make_shared<Builder>();
    out->openObject();
    out->add("endpoint", VPackValue(config.endpoint()));
    out->add("id", VPackValue(config.id()));
    out->add("pool", VPackValue(VPackValueType::Object));
    for (auto const& i : config.pool()) {
      out->add(i.first, VPackValue(i.second));
    }
    out->close();
    out->close();

    std::string path = privApiPrefix + "gossip";

    // gossip peers
    for (auto const& p : config.gossipPeers()) {
      if (p != config.endpoint()) {
        std::string clientid = config.id() + std::to_string(i++);
        auto hf =
            std::make_unique<std::unordered_map<std::string, std::string>>();
        arangodb::ClusterComm::instance()->asyncRequest(
          clientid, 1, p, rest::RequestType::POST, path,
          std::make_shared<std::string>(out->toJson()), hf,
          std::make_shared<GossipCallback>(_agent), 1.0, true, 0.5);
      }
    }

    // pool entries
    for (auto const& pair : config.pool()) {
      if (pair.second != config.endpoint()) {
        std::string clientid = config.id() + std::to_string(i++);
        auto hf =
            std::make_unique<std::unordered_map<std::string, std::string>>();
        arangodb::ClusterComm::instance()->asyncRequest(
          clientid, 1, pair.second, rest::RequestType::POST, path,
          std::make_shared<std::string>(out->toJson()), hf,
          std::make_shared<GossipCallback>(_agent), 1.0, true, 0.5);
      }
    }

    // don't panic
    _cv.wait(100000);

    // Timed out? :(
    if ((std::chrono::system_clock::now() - s) > timeout) {
      if (config.poolComplete()) {
        LOG_TOPIC(DEBUG, Logger::AGENCY) << "Stopping active gossipping!";
      } else {
        LOG_TOPIC(ERR, Logger::AGENCY)
          << "Failed to find complete pool of agents. Giving up!";
      }
      break;
    }

    // We're done
    if (config.poolComplete()) {
      _agent->startConstituent();
      break;
    }

  }

}

// @brief Active agency from persisted database
bool Inception::activeAgencyFromPersistence() {

  auto myConfig = _agent->config();
  std::string const path = pubApiPrefix + "config";

  // Can only be done responcibly, if we are complete
  if (myConfig.poolComplete()) {

    // Contact hosts on pool in hopes of finding a leader Id
    for (auto const& pair : myConfig.pool()) {

      if (pair.first != myConfig.id()) {

        auto comres = arangodb::ClusterComm::instance()->syncRequest(
          myConfig.id(), 1, pair.second, rest::RequestType::GET, path,
          std::string(), std::unordered_map<std::string, std::string>(), 1.0);

        if (comres->status == CL_COMM_SENT) {

          auto body = comres->result->getBodyVelocyPack();
          auto theirConfig = body->slice();

          std::string leaderId;

          // LeaderId in configuration?
          try {
            leaderId = theirConfig.get("leaderId").copyString();
          } catch (std::exception const& e) {
            LOG_TOPIC(DEBUG, Logger::AGENCY)
              << "Failed to get leaderId from" << pair.second << ": "
              << e.what();
          }

          if (leaderId != "") { // Got leaderId. Let's get do it.

            try {
              LOG_TOPIC(DEBUG, Logger::AGENCY)
                << "Found active agency with leader " << leaderId
                << " at endpoint "
                << theirConfig.get("configuration").get(
                  "pool").get(leaderId).copyString();
            } catch (std::exception const& e) {
              LOG_TOPIC(DEBUG, Logger::AGENCY)
                << "Failed to get leaderId from" << pair.second << ": "
                << e.what();
            }

            auto agency = std::make_shared<Builder>();
            agency->openObject();
            agency->add("term", theirConfig.get("term"));
            agency->add("id", VPackValue(leaderId));
            agency->add("active", theirConfig.get("configuration").get("active"));
            agency->add("pool", theirConfig.get("configuration").get("pool"));
            agency->close();
            _agent->notify(agency);

            return true;

          } else { // No leaderId. Move on.

            LOG_TOPIC(DEBUG, Logger::AGENCY)
              << "Failed to get leaderId from" << pair.second;

          }

        }

      }

    }
  }

  return false;

}

bool Inception::restartingActiveAgent() {

  auto myConfig = _agent->config();
  std::string const path = pubApiPrefix + "config";

  auto s = std::chrono::system_clock::now();
  std::chrono::seconds timeout(60);

  // Can only be done responcibly, if we are complete
  if (myConfig.poolComplete()) {

    auto pool = myConfig.pool();
    auto active = myConfig.active();

    CONDITION_LOCKER(guard, _cv);

    while (!this->isStopping() && !_agent->isStopping()) {

      active.erase(
        std::remove(active.begin(), active.end(), myConfig.id()), active.end());
      active.erase(
        std::remove(active.begin(), active.end(), ""), active.end());

      if (active.empty()) {
        return true;
      }

      for (auto& i : active) {

        if (i != myConfig.id() && i != "") {

          auto clientId = myConfig.id();
          auto comres   = arangodb::ClusterComm::instance()->syncRequest(
            clientId, 1, pool.at(i), rest::RequestType::GET, path, std::string(),
            std::unordered_map<std::string, std::string>(), 2.0);

          if (comres->status == CL_COMM_SENT) {

            try {

              auto theirActive = comres->result->getBodyVelocyPack()->
                slice().get("configuration").get("active").toJson();
              auto myActive = myConfig.activeToBuilder()->toJson();

              if (theirActive != myActive) {
                LOG_TOPIC(FATAL, Logger::AGENCY)
                  << "Assumed active RAFT peer and I disagree on active membership."
                  << "Administrative intervention needed.";
                FATAL_ERROR_EXIT();
                return false;
              } else {
                i = "";
              }

            } catch (std::exception const& e) {
              LOG_TOPIC(FATAL, Logger::AGENCY)
                << "Assumed active RAFT peer has no active agency list: " << e.what()
                << "Administrative intervention needed.";
              FATAL_ERROR_EXIT();
              return false;
            }
          }
        }

      }

      // Timed out? :(
      if ((std::chrono::system_clock::now() - s) > timeout) {
        if (myConfig.poolComplete()) {
          LOG_TOPIC(DEBUG, Logger::AGENCY) << "Stopping active gossipping!";
        } else {
          LOG_TOPIC(ERR, Logger::AGENCY)
            << "Failed to find complete pool of agents. Giving up!";
        }
        break;
      }

      _cv.wait(500000);

    }
  }

  return false;

}

inline static int64_t timeStamp() {
  using namespace std::chrono;
  return duration_cast<microseconds>(
    steady_clock::now().time_since_epoch()).count();
}

void Inception::reportIn(std::string const& peerId, uint64_t start) {
  MUTEX_LOCKER(lock, _pLock);
  _pings.push_back(1.0e-3*(double)(timeStamp()-start));
}

void Inception::reportIn(query_t const& query) {

  VPackSlice slice = query->slice();

  TRI_ASSERT(slice.isObject());
  TRI_ASSERT(slice.hasKey("mean"));
  TRI_ASSERT(slice.hasKey("stdev"));
  TRI_ASSERT(slice.hasKey("min"));
  TRI_ASSERT(slice.hasKey("max"));

  MUTEX_LOCKER(lock, _mLock);
  _measurements.push_back(
    std::vector<double>(
      {slice.get("mean").getDouble(), slice.get("stdev").getDouble(),
          slice.get("max").getDouble(), slice.get("min").getDouble()} ));

}

bool Inception::estimateRAFTInterval() {

  using namespace std::chrono;

  std::string path("/_api/agency/config");
  auto pool = _agent->config().pool();
  auto myid = _agent->id();

  for (size_t i = 0; i < 25; ++i) {
    for (auto const& peer : pool) {
      if (peer.first != myid) {
        std::string clientid = peer.first + std::to_string(i);
        auto hf =
          std::make_unique<std::unordered_map<std::string, std::string>>();
        arangodb::ClusterComm::instance()->asyncRequest(
          clientid, 1, peer.second, rest::RequestType::GET, path,
          std::make_shared<std::string>(), hf,
          std::make_shared<MeasureCallback>(this, peer.second, timeStamp()),
          2.0, true);
      }
    }
    std::this_thread::sleep_for(std::chrono::duration<double,std::milli>(5));
  }

  auto s = system_clock::now();
  seconds timeout(3);

  CONDITION_LOCKER(guard, _cv);

  while (true) {

    _cv.wait(50000);

    {
      MUTEX_LOCKER(lock, _pLock);
      if (_pings.size() == 25*(pool.size()-1)) {
        LOG_TOPIC(DEBUG, Logger::AGENCY) << "All pings are in";
        break;
      }
    }

    if ((system_clock::now() - s) > timeout) {
      LOG_TOPIC(DEBUG, Logger::AGENCY) << "Timed out waiting for pings";
      break;
    }

  }

  double sum, mean = 0., sq_sum, stdev = 0., mx = 0., mn = 0.;

  try {

    MUTEX_LOCKER(lock, _pLock);
    size_t num = _pings.size();
    sum    = std::accumulate(_pings.begin(), _pings.end(), 0.0);
    mean   = sum / num;
    mx     = *std::max_element(_pings.begin(), _pings.end());
    mn     = *std::min_element(_pings.begin(), _pings.end());
    std::transform(_pings.begin(), _pings.end(), _pings.begin(),
                   std::bind2nd(std::minus<double>(), mean));
    sq_sum =
      std::inner_product(_pings.begin(), _pings.end(), _pings.begin(), 0.0);
    stdev = std::sqrt(sq_sum / num);

    LOG_TOPIC(DEBUG, Logger::AGENCY)
      << "mean(" << mean << ") stdev(" << stdev<< ")";

  } catch (std::exception const& e) {
    LOG_TOPIC(WARN, Logger::AGENCY) << e.what();
  }

  Builder measurement;
  measurement.openObject();
  measurement.add("mean", VPackValue(mean));
  measurement.add("stdev", VPackValue(stdev));
  measurement.add("min", VPackValue(mn));
  measurement.add("max", VPackValue(mx));
  measurement.close();
  std::string measjson = measurement.toJson();

  path = privApiPrefix + "measure";
  for (auto const& peer : pool) {
    if (peer.first != myid) {
      auto clientId = "1";
      auto comres   = arangodb::ClusterComm::instance()->syncRequest(
        clientId, 1, peer.second, rest::RequestType::POST, path,
        measjson, std::unordered_map<std::string, std::string>(), 2.0);
    }
  }

  {
    MUTEX_LOCKER(lock, _mLock);
    _measurements.push_back(std::vector<double>({mean, stdev, mx, mn}));
  }
  s = system_clock::now();
  while (true) {

    _cv.wait(50000);

    {
      MUTEX_LOCKER(lock, _mLock);
      if (_measurements.size() == pool.size()) {
        LOG_TOPIC(DEBUG, Logger::AGENCY) << "All measurements are in";
        break;
      }
    }

    if ((system_clock::now() - s) > timeout) {
      LOG_TOPIC(WARN, Logger::AGENCY)
        << "Timed out waiting for other measurements. Auto-adaptation failed! Will stick to command line arguments";
      return false;
    }

  }

  double maxmean  = .0;
  double maxstdev = .0;
  for (auto const& meas : _measurements) {
    if (maxmean < meas[0]) {
      maxmean = meas[0];
    }
    if (maxstdev < meas[1]) {
      maxstdev = meas[1];
    }
  }

  maxmean = 1.0e-2*std::ceil(100*(.15 + 1.0e-3*maxmean));

  LOG_TOPIC(INFO, Logger::AGENCY)
    << "Auto-adapting RAFT timing to: {" << maxmean
    << ", " << 5.0*maxmean << "}s";

  _agent->resetRAFTTimes(maxmean, 5.0*maxmean);

  return true;

}

// @brief Active agency from persisted database
bool Inception::activeAgencyFromCommandLine() {
  return false;
}

// @brief Thread main
void Inception::run() {

  // 1. If active agency, do as you're told
  if (activeAgencyFromPersistence()) {
    _agent->ready(true);
  }

  // 2. If we think that we used to be active agent
  if (!_agent->ready() && restartingActiveAgent()) {
    _agent->ready(true);
  }

  // 3. Else gossip
  config_t config = _agent->config();
  if (!_agent->ready() && !config.poolComplete()) {
    gossip();
  }

  // 4. If still incomplete bail out :(
  config = _agent->config();
  if (!_agent->ready() && !config.poolComplete()) {
    LOG_TOPIC(FATAL, Logger::AGENCY)
      << "Failed to build environment for RAFT algorithm. Bailing out!";
    FATAL_ERROR_EXIT();
  }

  estimateRAFTInterval();

  _agent->ready(true);

}

// @brief Graceful shutdown
void Inception::beginShutdown() {
  Thread::beginShutdown();
  CONDITION_LOCKER(guard, _cv);
  guard.broadcast();
}
<|endoftext|>


<commit_before>#include "persistentstore.h"

WARNINGS_DISABLE
#include <QFile>
#include <QFileInfo>

static QMutex mutex;
WARNINGS_ENABLE

#include "debug.h"

#include <TSettings.h>

#define DEFAULT_DBNAME "tarsnap.db"

PersistentStore *global_store = nullptr;

bool PersistentStore::_initialized = false;

void PersistentStore::initializePersistentStore()
{
    if(global_store == nullptr)
        global_store = new PersistentStore();
}

void PersistentStore::destroy()
{
    Q_ASSERT(global_store != nullptr);

    // Clean up.
    delete global_store;
    global_store = nullptr;
}

PersistentStore::PersistentStore()
{
}

bool PersistentStore::init()
{
    QMutexLocker locker(&mutex);

    // Get application data directory and database filename.
    TSettings settings;
    QString   appdata = settings.value("app/app_data", "").toString();
    if(appdata.isEmpty())
    {
        DEBUG << "Error creating the PersistentStore: app.appdata not set.";
        return false;
    }
    bool      create = false;
    QString   dbUrl(appdata + QDir::separator() + DEFAULT_DBNAME);
    QFileInfo dbFileInfo(dbUrl);

    // Initialize database object.
    QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE", "tarsnap");
    db.setConnectOptions("QSQLITE_OPEN_URI");
    db.setDatabaseName(dbUrl);

    // Determine whether to try to open the database.
    if(!dbFileInfo.exists())
    {
        create = true;
    }
    else if(!dbFileInfo.isFile() || !dbFileInfo.isReadable())
    {
        DEBUG
            << "Error creating the PersistentStore: DB file is not accessible "
            << dbUrl;
        return false;
    } // Database file exists and is readable; attempt to open.
    else if(!db.open())
    {
        DEBUG << "Error opening the PersistentStore DB: "
              << db.lastError().text();
        return false;
    }
    else
    {
        // Successfully opened database.
        QStringList tables = db.tables();
        // Is the database valid?
        if(!tables.contains("archives", Qt::CaseInsensitive))
        {
            db.close();
            DEBUG << "Invalid PersistentStore DB found. Attempting to recover.";
            QString newName(
                dbUrl + "."
                + QString::number(QDateTime::currentMSecsSinceEpoch()));
            if(!QFile::rename(dbUrl, newName))
            {
                DEBUG << "Failed to rename current invalid PersistentStore DB. "
                         "Please manually clean up the DB directory "
                      << appdata;
                return false;
            }
            create = true;
        }
        else
        {
            // Check the database version, and upgrade if necessary.
            if(!tables.contains("version", Qt::CaseInsensitive))
            {
                if(!upgradeVersion0())
                {
                    DEBUG << "Failed to upgrade PersistentStore DB. It's best "
                             "to start from scratch by purging the existing DB "
                             "in "
                          << appdata;
                    return false;
                }
            }
            int       version = -1;
            QSqlQuery query(db);
            if(query.exec("SELECT version FROM version"))
            {
                query.next();
                version = query.value(0).toInt();
            }
            else
            {
                DEBUG << "Failed to get current DB version: "
                      << query.lastError().text();
                return false;
            }
            if((version == 0) && upgradeVersion1())
            {
                DEBUG << "DB upgraded to version 1.";
                version = 1;
            }
            if((version == 1) && upgradeVersion2())
            {
                DEBUG << "DB upgraded to version 2.";
                version = 2;
            }
            if((version == 2) && upgradeVersion3())
            {
                DEBUG << "DB upgraded to version 3.";
                version = 3;
            }
            if((version == 3) && upgradeVersion4())
            {
                DEBUG << "DB upgraded to version 4.";
                version = 4;
            }
            (void)version; /* not used beyond this point. */
        }
    }

    // Create new database (if needed).
    if(create)
    {
        QFile dbTemplate(":/dbtemplate.db");

        // Create the directory for the db (if necessary)
        QDir dbDir = dbFileInfo.absoluteDir();
        if(!dbDir.exists())
            dbDir.mkpath(dbDir.absolutePath());

        if(!dbTemplate.exists())
            DEBUG << "DB template not found; check the resources file";
        if(!dbTemplate.copy(dbUrl))
        {
            DEBUG << "Failed to create the PersistentStore DB.";
            return false;
        }
        // Work around the fact that QFile::copy from the resource system does
        // not set u+w on the resulted file
        QFile dbFile(dbUrl);
        dbFile.setPermissions(dbFile.permissions() | QFileDevice::WriteOwner);
        dbFile.close();
        if(!db.open())
        {
            DEBUG << "Error opening the PersistentStore DB: "
                  << db.lastError().text();
            return false;
        }
    }
    return _initialized = true;
}

void PersistentStore::deinit()
{
    QMutexLocker locker(&mutex);

    if(_initialized)
    {
        QSqlDatabase::removeDatabase("tarsnap");
        _initialized = false;
    }
}

QSqlQuery PersistentStore::createQuery()
{
    if(_initialized)
    {
        QSqlDatabase db = QSqlDatabase::database("tarsnap");
        return QSqlQuery(db);
    }
    else
    {
        DEBUG << "PersistentStore not initialized.";
        return QSqlQuery();
    }
}

void PersistentStore::purge()
{
    if(_initialized)
    {
        QString dbUrl;
        {
            QSqlDatabase db = QSqlDatabase::database("tarsnap");
            dbUrl           = db.databaseName();
        }
        deinit();
        QFile dbFile(dbUrl);
        if(dbFile.exists())
            dbFile.remove();
        else
            DEBUG << "DB file not accessible: " << dbUrl;
    }
    else
    {
        DEBUG << "DB not initialized.";
    }
}

bool PersistentStore::runQuery(QSqlQuery query)
{
    QMutexLocker locker(&mutex);

    bool result = false;
    if(_initialized)
    {
        if(!query.exec())
            DEBUG << query.lastError().text();
        else
            result = true;
    }
    else
    {
        DEBUG << "DB not initialized.";
    }

    return result;
}

/*
 * The below "if((result =" lines don't play well with clang-format.
 * I intend to rewrite them once I have a test suite for checking the
 * version upgrading.
 */
/* clang-format off */
bool PersistentStore::upgradeVersion0()
{
    bool      result = false;
    QSqlDatabase db = QSqlDatabase::database("tarsnap");
    QSqlQuery query(db);

    if((result = query.exec("CREATE TABLE version (version INTEGER NOT NULL);")))
        result = query.exec("INSERT INTO version VALUES (0);");

    if(!result)
    {
        DEBUG << query.lastError().text();
        DEBUG << "Failed to upgrade DB to version 0." << db;
    }
    return result;
}

bool PersistentStore::upgradeVersion1()
{
    bool      result = false;
    QSqlDatabase db = QSqlDatabase::database("tarsnap");
    QSqlQuery query(db);

    if((result = query.exec("ALTER TABLE jobs ADD COLUMN optionScheduledEnabled INTEGER;")))
        result = query.exec("UPDATE version SET version = 1;");

    // Handle the special case, since I started versioning DB after two app
    // releases and this change in between...
    if(!result && query.lastError().text().contains("duplicate column name"))
        result = query.exec("UPDATE version SET version = 1;");

    if(!result)
    {
        DEBUG << query.lastError().text();
        DEBUG << "Failed to upgrade DB to version 1." << db.databaseName();
    }
    return result;
}

bool PersistentStore::upgradeVersion2()
{
    bool      result = false;
    QSqlDatabase db = QSqlDatabase::database("tarsnap");
    QSqlQuery query(db);

    if((result = query.exec("ALTER TABLE jobs ADD COLUMN optionSkipFiles INTEGER;")))
    if((result = query.exec("ALTER TABLE jobs ADD COLUMN optionSkipFilesPatterns TEXT;")))
        result = query.exec("UPDATE version SET version = 2;");

    if(!result)
    {
        DEBUG << query.lastError().text();
        DEBUG << "Failed to upgrade DB to version 2." << db.databaseName();
    }
    return result;
}

bool PersistentStore::upgradeVersion3()
{
    bool      result = false;
    QSqlDatabase db = QSqlDatabase::database("tarsnap");
    QSqlQuery query(db);

    if((result = query.exec("ALTER TABLE jobs ADD COLUMN optionSkipNoDump INTEGER;")))
    if((result = query.exec("UPDATE archives SET contents=\"\";")))
    if((result = query.exec("CREATE TABLE journal (timestamp INTEGER NOT NULL, log TEXT);")))
        result = query.exec("UPDATE version SET version = 3;");

    if(!result)
    {
        DEBUG << query.lastError().text();
        DEBUG << "Failed to upgrade DB to version 3." << db.databaseName();
    }
    return result;
}

bool PersistentStore::upgradeVersion4()
{
    bool      result = false;
    QSqlDatabase db = QSqlDatabase::database("tarsnap");
    QSqlQuery query(db);

    if((result = query.exec("UPDATE archives SET contents=\"\";")))
    if((result = query.exec("ALTER TABLE archives ADD COLUMN truncated INTEGER;")))
    if((result = query.exec("ALTER TABLE archives ADD COLUMN truncatedInfo TEXT;")))
    if((result = query.exec("ALTER TABLE jobs ADD COLUMN settingShowHidden INTEGER;")))
    if((result = query.exec("ALTER TABLE jobs ADD COLUMN settingShowSystem INTEGER;")))
    if((result = query.exec("ALTER TABLE jobs ADD COLUMN settingHideSymlinks INTEGER;")))
    if((result = query.exec("UPDATE jobs SET optionScheduledEnabled=0;")))
        result = query.exec("UPDATE version SET version = 4;");

    if(!result)
    {
        DEBUG << query.lastError().text();
        DEBUG << "Failed to upgrade DB to version 4." << db.databaseName();
    }
    return result;
}
/* clang-format on */
<commit_msg>PersistentStore: allow ::destroy() without init<commit_after>#include "persistentstore.h"

WARNINGS_DISABLE
#include <QFile>
#include <QFileInfo>

static QMutex mutex;
WARNINGS_ENABLE

#include "debug.h"

#include <TSettings.h>

#define DEFAULT_DBNAME "tarsnap.db"

PersistentStore *global_store = nullptr;

bool PersistentStore::_initialized = false;

void PersistentStore::initializePersistentStore()
{
    if(global_store == nullptr)
        global_store = new PersistentStore();
}

void PersistentStore::destroy()
{
    // This will happen if init_shared.cpp produces INIT_NEEDS_SETUP -- it
    // bails early from init_shared().
    if(global_store == nullptr)
        return;

    // Clean up.
    delete global_store;
    global_store = nullptr;
}

PersistentStore::PersistentStore()
{
}

bool PersistentStore::init()
{
    QMutexLocker locker(&mutex);

    // Get application data directory and database filename.
    TSettings settings;
    QString   appdata = settings.value("app/app_data", "").toString();
    if(appdata.isEmpty())
    {
        DEBUG << "Error creating the PersistentStore: app.appdata not set.";
        return false;
    }
    bool      create = false;
    QString   dbUrl(appdata + QDir::separator() + DEFAULT_DBNAME);
    QFileInfo dbFileInfo(dbUrl);

    // Initialize database object.
    QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE", "tarsnap");
    db.setConnectOptions("QSQLITE_OPEN_URI");
    db.setDatabaseName(dbUrl);

    // Determine whether to try to open the database.
    if(!dbFileInfo.exists())
    {
        create = true;
    }
    else if(!dbFileInfo.isFile() || !dbFileInfo.isReadable())
    {
        DEBUG
            << "Error creating the PersistentStore: DB file is not accessible "
            << dbUrl;
        return false;
    } // Database file exists and is readable; attempt to open.
    else if(!db.open())
    {
        DEBUG << "Error opening the PersistentStore DB: "
              << db.lastError().text();
        return false;
    }
    else
    {
        // Successfully opened database.
        QStringList tables = db.tables();
        // Is the database valid?
        if(!tables.contains("archives", Qt::CaseInsensitive))
        {
            db.close();
            DEBUG << "Invalid PersistentStore DB found. Attempting to recover.";
            QString newName(
                dbUrl + "."
                + QString::number(QDateTime::currentMSecsSinceEpoch()));
            if(!QFile::rename(dbUrl, newName))
            {
                DEBUG << "Failed to rename current invalid PersistentStore DB. "
                         "Please manually clean up the DB directory "
                      << appdata;
                return false;
            }
            create = true;
        }
        else
        {
            // Check the database version, and upgrade if necessary.
            if(!tables.contains("version", Qt::CaseInsensitive))
            {
                if(!upgradeVersion0())
                {
                    DEBUG << "Failed to upgrade PersistentStore DB. It's best "
                             "to start from scratch by purging the existing DB "
                             "in "
                          << appdata;
                    return false;
                }
            }
            int       version = -1;
            QSqlQuery query(db);
            if(query.exec("SELECT version FROM version"))
            {
                query.next();
                version = query.value(0).toInt();
            }
            else
            {
                DEBUG << "Failed to get current DB version: "
                      << query.lastError().text();
                return false;
            }
            if((version == 0) && upgradeVersion1())
            {
                DEBUG << "DB upgraded to version 1.";
                version = 1;
            }
            if((version == 1) && upgradeVersion2())
            {
                DEBUG << "DB upgraded to version 2.";
                version = 2;
            }
            if((version == 2) && upgradeVersion3())
            {
                DEBUG << "DB upgraded to version 3.";
                version = 3;
            }
            if((version == 3) && upgradeVersion4())
            {
                DEBUG << "DB upgraded to version 4.";
                version = 4;
            }
            (void)version; /* not used beyond this point. */
        }
    }

    // Create new database (if needed).
    if(create)
    {
        QFile dbTemplate(":/dbtemplate.db");

        // Create the directory for the db (if necessary)
        QDir dbDir = dbFileInfo.absoluteDir();
        if(!dbDir.exists())
            dbDir.mkpath(dbDir.absolutePath());

        if(!dbTemplate.exists())
            DEBUG << "DB template not found; check the resources file";
        if(!dbTemplate.copy(dbUrl))
        {
            DEBUG << "Failed to create the PersistentStore DB.";
            return false;
        }
        // Work around the fact that QFile::copy from the resource system does
        // not set u+w on the resulted file
        QFile dbFile(dbUrl);
        dbFile.setPermissions(dbFile.permissions() | QFileDevice::WriteOwner);
        dbFile.close();
        if(!db.open())
        {
            DEBUG << "Error opening the PersistentStore DB: "
                  << db.lastError().text();
            return false;
        }
    }
    return _initialized = true;
}

void PersistentStore::deinit()
{
    QMutexLocker locker(&mutex);

    if(_initialized)
    {
        QSqlDatabase::removeDatabase("tarsnap");
        _initialized = false;
    }
}

QSqlQuery PersistentStore::createQuery()
{
    if(_initialized)
    {
        QSqlDatabase db = QSqlDatabase::database("tarsnap");
        return QSqlQuery(db);
    }
    else
    {
        DEBUG << "PersistentStore not initialized.";
        return QSqlQuery();
    }
}

void PersistentStore::purge()
{
    if(_initialized)
    {
        QString dbUrl;
        {
            QSqlDatabase db = QSqlDatabase::database("tarsnap");
            dbUrl           = db.databaseName();
        }
        deinit();
        QFile dbFile(dbUrl);
        if(dbFile.exists())
            dbFile.remove();
        else
            DEBUG << "DB file not accessible: " << dbUrl;
    }
    else
    {
        DEBUG << "DB not initialized.";
    }
}

bool PersistentStore::runQuery(QSqlQuery query)
{
    QMutexLocker locker(&mutex);

    bool result = false;
    if(_initialized)
    {
        if(!query.exec())
            DEBUG << query.lastError().text();
        else
            result = true;
    }
    else
    {
        DEBUG << "DB not initialized.";
    }

    return result;
}

/*
 * The below "if((result =" lines don't play well with clang-format.
 * I intend to rewrite them once I have a test suite for checking the
 * version upgrading.
 */
/* clang-format off */
bool PersistentStore::upgradeVersion0()
{
    bool      result = false;
    QSqlDatabase db = QSqlDatabase::database("tarsnap");
    QSqlQuery query(db);

    if((result = query.exec("CREATE TABLE version (version INTEGER NOT NULL);")))
        result = query.exec("INSERT INTO version VALUES (0);");

    if(!result)
    {
        DEBUG << query.lastError().text();
        DEBUG << "Failed to upgrade DB to version 0." << db;
    }
    return result;
}

bool PersistentStore::upgradeVersion1()
{
    bool      result = false;
    QSqlDatabase db = QSqlDatabase::database("tarsnap");
    QSqlQuery query(db);

    if((result = query.exec("ALTER TABLE jobs ADD COLUMN optionScheduledEnabled INTEGER;")))
        result = query.exec("UPDATE version SET version = 1;");

    // Handle the special case, since I started versioning DB after two app
    // releases and this change in between...
    if(!result && query.lastError().text().contains("duplicate column name"))
        result = query.exec("UPDATE version SET version = 1;");

    if(!result)
    {
        DEBUG << query.lastError().text();
        DEBUG << "Failed to upgrade DB to version 1." << db.databaseName();
    }
    return result;
}

bool PersistentStore::upgradeVersion2()
{
    bool      result = false;
    QSqlDatabase db = QSqlDatabase::database("tarsnap");
    QSqlQuery query(db);

    if((result = query.exec("ALTER TABLE jobs ADD COLUMN optionSkipFiles INTEGER;")))
    if((result = query.exec("ALTER TABLE jobs ADD COLUMN optionSkipFilesPatterns TEXT;")))
        result = query.exec("UPDATE version SET version = 2;");

    if(!result)
    {
        DEBUG << query.lastError().text();
        DEBUG << "Failed to upgrade DB to version 2." << db.databaseName();
    }
    return result;
}

bool PersistentStore::upgradeVersion3()
{
    bool      result = false;
    QSqlDatabase db = QSqlDatabase::database("tarsnap");
    QSqlQuery query(db);

    if((result = query.exec("ALTER TABLE jobs ADD COLUMN optionSkipNoDump INTEGER;")))
    if((result = query.exec("UPDATE archives SET contents=\"\";")))
    if((result = query.exec("CREATE TABLE journal (timestamp INTEGER NOT NULL, log TEXT);")))
        result = query.exec("UPDATE version SET version = 3;");

    if(!result)
    {
        DEBUG << query.lastError().text();
        DEBUG << "Failed to upgrade DB to version 3." << db.databaseName();
    }
    return result;
}

bool PersistentStore::upgradeVersion4()
{
    bool      result = false;
    QSqlDatabase db = QSqlDatabase::database("tarsnap");
    QSqlQuery query(db);

    if((result = query.exec("UPDATE archives SET contents=\"\";")))
    if((result = query.exec("ALTER TABLE archives ADD COLUMN truncated INTEGER;")))
    if((result = query.exec("ALTER TABLE archives ADD COLUMN truncatedInfo TEXT;")))
    if((result = query.exec("ALTER TABLE jobs ADD COLUMN settingShowHidden INTEGER;")))
    if((result = query.exec("ALTER TABLE jobs ADD COLUMN settingShowSystem INTEGER;")))
    if((result = query.exec("ALTER TABLE jobs ADD COLUMN settingHideSymlinks INTEGER;")))
    if((result = query.exec("UPDATE jobs SET optionScheduledEnabled=0;")))
        result = query.exec("UPDATE version SET version = 4;");

    if(!result)
    {
        DEBUG << query.lastError().text();
        DEBUG << "Failed to upgrade DB to version 4." << db.databaseName();
    }
    return result;
}
/* clang-format on */
<|endoftext|>


<commit_before>/*
Copyright 2015 Google Inc. All rights reserved.

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

#include <cstdlib>
#include <cstring>

#include <exception>
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>

extern "C" {
    #include "libjsonnet.h"
}

#include "parser.h"
#include "static_analysis.h"
#include "vm.h"

static void memory_panic(void)
{
    fputs("FATAL ERROR: A memory allocation error occurred.\n", stderr);
    abort();
}

static char *from_string(JsonnetVm* vm, const std::string &v)
{
    char *r = jsonnet_realloc(vm, nullptr, v.length() + 1);
    std::strcpy(r, v.c_str());
    return r;
}

/** Resolve the absolute path and use C++ file io to load the file.
 */
static char *default_import_callback(void *ctx, const char *base, const char *file, int *success)
{
    auto *vm = static_cast<JsonnetVm*>(ctx);

    if (std::strlen(file) == 0) {
        *success = 0;
        return from_string(vm, "The empty string is not a valid filename");
    }

    if (file[std::strlen(file) - 1] == '/') {
        *success = 0;
        return from_string(vm, "Attempted to import a directory");
    }

    std::string abs_path;
    // It is possible that file is an absolute path
    if (file[0] == '/')
        abs_path = file;
    else
        abs_path = std::string(base) + file;

    std::ifstream f;
    f.open(abs_path.c_str());
    if (!f.good()) {
        *success = 0;
        return from_string(vm, std::strerror(errno));
    }
    try {
        std::string input;
        input.assign(std::istreambuf_iterator<char>(f), std::istreambuf_iterator<char>());
        *success = 1;
        return from_string(vm, input);
    } catch (const std::ios_base::failure &io_err) {
        *success = 0;
        return from_string(vm, io_err.what());
    }
}

struct JsonnetVm {
    double gcGrowthTrigger;
    unsigned maxStack;
    unsigned gcMinObjects;
    bool debugAst;
    unsigned maxTrace;
    std::map<std::string, std::string> extVars;
    JsonnetImportCallback *importCallback;
    void *importCallbackContext;
    bool stringOutput;
    JsonnetVm(void)
      : gcGrowthTrigger(2.0), maxStack(500), gcMinObjects(1000), debugAst(false), maxTrace(20),
        importCallback(default_import_callback), importCallbackContext(this), stringOutput(false)
    { }
};

#define TRY try {
#define CATCH(func) \
    } catch (const std::bad_alloc &) {\
        memory_panic(); \
    } catch (const std::exception &e) {\
        std::cerr << "Something went wrong during " func ", please report this: " \
                  << e.what() << std::endl; \
        abort(); \
    }

JsonnetVm *jsonnet_make(void)
{
    TRY
    return new JsonnetVm();
    CATCH("jsonnet_make")
    return nullptr;
}

void jsonnet_destroy(JsonnetVm *vm)
{
    TRY
    delete vm;
    CATCH("jsonnet_destroy")
}

void jsonnet_max_stack(JsonnetVm *vm, unsigned v)
{
    vm->maxStack = v;
}

void jsonnet_gc_min_objects(JsonnetVm *vm, unsigned v)
{
    vm->gcMinObjects = v;
}

void jsonnet_gc_growth_trigger(JsonnetVm *vm, double v)
{
    vm->gcGrowthTrigger = v;
}

void jsonnet_string_output(struct JsonnetVm *vm, int v)
{
    vm->stringOutput = bool(v);
}

void jsonnet_import_callback(struct JsonnetVm *vm, JsonnetImportCallback *cb, void *ctx)
{
    vm->importCallback = cb;
    vm->importCallbackContext = ctx;
}

void jsonnet_ext_var(JsonnetVm *vm, const char *key, const char *val)
{
    vm->extVars[key] = val;
}

void jsonnet_debug_ast(JsonnetVm *vm, int v)
{
    vm->debugAst = v;
}

void jsonnet_max_trace(JsonnetVm *vm, unsigned v)
{
    vm->maxTrace = v;
}

static char *jsonnet_evaluate_snippet_aux(JsonnetVm *vm, const char *filename,
                                          const char *snippet, int *error, bool multi)
{
    try {
        Allocator alloc;
        AST *expr = jsonnet_parse(&alloc, filename, snippet);
        std::string json_str;
        std::map<std::string, std::string> files;
        if (vm->debugAst) {
            json_str = jsonnet_unparse_jsonnet(expr);
        } else {
            jsonnet_static_analysis(expr);
            if (multi) {
                files = jsonnet_vm_execute_multi(&alloc, expr, vm->extVars, vm->maxStack,
                                                 vm->gcMinObjects, vm->gcGrowthTrigger,
                                                 vm->importCallback, vm->importCallbackContext,
                                                 vm->stringOutput);
            } else {
                json_str = jsonnet_vm_execute(&alloc, expr, vm->extVars, vm->maxStack,
                                              vm->gcMinObjects, vm->gcGrowthTrigger,
                                              vm->importCallback, vm->importCallbackContext,
                                              vm->stringOutput);
            }
        }
        if (multi) {
            size_t sz = 1; // final sentinel
            for (const auto &pair : files) {
                sz += pair.first.length() + 1; // include sentinel
                sz += pair.second.length() + 2; // Add a '\n' as well as sentinel
            }
            char *buf = (char*)::malloc(sz);
            if (buf == nullptr) memory_panic();
            std::ptrdiff_t i = 0;
            for (const auto &pair : files) {
                memcpy(&buf[i], pair.first.c_str(), pair.first.length() + 1);
                i += pair.first.length() + 1;
                memcpy(&buf[i], pair.second.c_str(), pair.second.length());
                i += pair.second.length();
                buf[i] = '\n';
                i++;
                buf[i] = '\0';
                i++;
            }
            buf[i] = '\0'; // final sentinel
            *error = false;
            return buf;
        } else {
            json_str += "\n";
            *error = false;
            return from_string(vm, json_str);
        }

    } catch (StaticError &e) {
        std::stringstream ss;
        ss << "STATIC ERROR: " << e << std::endl;
        *error = true;
        return from_string(vm, ss.str());

    } catch (RuntimeError &e) {
        std::stringstream ss;
        ss << "RUNTIME ERROR: " << e.msg << std::endl;
        const long max_above = vm->maxTrace / 2;
        const long max_below = vm->maxTrace - max_above;
        const long sz = e.stackTrace.size();
        for (long i = 0 ; i < sz ; ++i) {
            const auto &f = e.stackTrace[i];
            if (vm->maxTrace > 0 && i >= max_above && i < sz - max_below) {
                if (i == max_above)
                    ss << "\t..." << std::endl;
            } else {
                ss << "\t" << f.location << "\t" << f.name << std::endl;
            }
        }
        *error = true;
        return from_string(vm, ss.str());
    }

}

static char *jsonnet_evaluate_file_aux(JsonnetVm *vm, const char *filename, int *error, bool multi)
{
    std::ifstream f;
    f.open(filename);
    if (!f.good()) {
        std::stringstream ss;
        ss << "Opening input file: " << filename << ": " << strerror(errno);
        *error = true;
        return ::strdup(ss.str().c_str());
    }
    std::string input;
    input.assign(std::istreambuf_iterator<char>(f),
                 std::istreambuf_iterator<char>());

    return jsonnet_evaluate_snippet_aux(vm, filename, input.c_str(), error, multi);
}

char *jsonnet_evaluate_file(JsonnetVm *vm, const char *filename, int *error)
{
    TRY
    return jsonnet_evaluate_file_aux(vm, filename, error, false);
    CATCH("jsonnet_evaluate_file")
    return nullptr;  // Never happens.
}

char *jsonnet_evaluate_file_multi(JsonnetVm *vm, const char *filename, int *error)
{
    TRY
    return jsonnet_evaluate_file_aux(vm, filename, error, true);
    CATCH("jsonnet_evaluate_file_multi")
    return nullptr;  // Never happens.
}

char *jsonnet_evaluate_snippet(JsonnetVm *vm, const char *filename, const char *snippet, int *error)
{
    TRY
    return jsonnet_evaluate_snippet_aux(vm, filename, snippet, error, false);
    CATCH("jsonnet_evaluate_snippet")
    return nullptr;  // Never happens.
}

char *jsonnet_evaluate_snippet_multi(JsonnetVm *vm, const char *filename,
                                     const char *snippet, int *error)
{
    TRY
    return jsonnet_evaluate_snippet_aux(vm, filename, snippet, error, true);
    CATCH("jsonnet_evaluate_snippet_multi")
    return nullptr;  // Never happens.
}

char *jsonnet_realloc(JsonnetVm *vm, char *str, size_t sz)
{
    (void) vm;
    if (str == nullptr) {
        if (sz == 0) return nullptr;
        auto *r = static_cast<char*>(::malloc(sz));
        if (r == nullptr) memory_panic();
        return r;
    } else {
        if (sz == 0) {
            ::free(str);
            return nullptr;
        } else {
            auto *r = static_cast<char*>(::realloc(str, sz));
            if (r == nullptr) memory_panic();
            return r;
        }
    }
}

<commit_msg>Handle error in one more case, fix #51<commit_after>/*
Copyright 2015 Google Inc. All rights reserved.

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

#include <cstdlib>
#include <cstring>

#include <exception>
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>

extern "C" {
    #include "libjsonnet.h"
}

#include "parser.h"
#include "static_analysis.h"
#include "vm.h"

static void memory_panic(void)
{
    fputs("FATAL ERROR: A memory allocation error occurred.\n", stderr);
    abort();
}

static char *from_string(JsonnetVm* vm, const std::string &v)
{
    char *r = jsonnet_realloc(vm, nullptr, v.length() + 1);
    std::strcpy(r, v.c_str());
    return r;
}

/** Resolve the absolute path and use C++ file io to load the file.
 */
static char *default_import_callback(void *ctx, const char *base, const char *file, int *success)
{
    auto *vm = static_cast<JsonnetVm*>(ctx);

    if (std::strlen(file) == 0) {
        *success = 0;
        return from_string(vm, "The empty string is not a valid filename");
    }

    if (file[std::strlen(file) - 1] == '/') {
        *success = 0;
        return from_string(vm, "Attempted to import a directory");
    }

    std::string abs_path;
    // It is possible that file is an absolute path
    if (file[0] == '/')
        abs_path = file;
    else
        abs_path = std::string(base) + file;

    std::ifstream f;
    f.open(abs_path.c_str());
    if (!f.good()) {
        *success = 0;
        return from_string(vm, std::strerror(errno));
    }
    try {
        std::string input;
        input.assign(std::istreambuf_iterator<char>(f), std::istreambuf_iterator<char>());
        *success = 1;
        return from_string(vm, input);
    } catch (const std::ios_base::failure &io_err) {
        *success = 0;
        return from_string(vm, io_err.what());
    }
}

struct JsonnetVm {
    double gcGrowthTrigger;
    unsigned maxStack;
    unsigned gcMinObjects;
    bool debugAst;
    unsigned maxTrace;
    std::map<std::string, std::string> extVars;
    JsonnetImportCallback *importCallback;
    void *importCallbackContext;
    bool stringOutput;
    JsonnetVm(void)
      : gcGrowthTrigger(2.0), maxStack(500), gcMinObjects(1000), debugAst(false), maxTrace(20),
        importCallback(default_import_callback), importCallbackContext(this), stringOutput(false)
    { }
};

#define TRY try {
#define CATCH(func) \
    } catch (const std::bad_alloc &) {\
        memory_panic(); \
    } catch (const std::exception &e) {\
        std::cerr << "Something went wrong during " func ", please report this: " \
                  << e.what() << std::endl; \
        abort(); \
    }

JsonnetVm *jsonnet_make(void)
{
    TRY
    return new JsonnetVm();
    CATCH("jsonnet_make")
    return nullptr;
}

void jsonnet_destroy(JsonnetVm *vm)
{
    TRY
    delete vm;
    CATCH("jsonnet_destroy")
}

void jsonnet_max_stack(JsonnetVm *vm, unsigned v)
{
    vm->maxStack = v;
}

void jsonnet_gc_min_objects(JsonnetVm *vm, unsigned v)
{
    vm->gcMinObjects = v;
}

void jsonnet_gc_growth_trigger(JsonnetVm *vm, double v)
{
    vm->gcGrowthTrigger = v;
}

void jsonnet_string_output(struct JsonnetVm *vm, int v)
{
    vm->stringOutput = bool(v);
}

void jsonnet_import_callback(struct JsonnetVm *vm, JsonnetImportCallback *cb, void *ctx)
{
    vm->importCallback = cb;
    vm->importCallbackContext = ctx;
}

void jsonnet_ext_var(JsonnetVm *vm, const char *key, const char *val)
{
    vm->extVars[key] = val;
}

void jsonnet_debug_ast(JsonnetVm *vm, int v)
{
    vm->debugAst = v;
}

void jsonnet_max_trace(JsonnetVm *vm, unsigned v)
{
    vm->maxTrace = v;
}

static char *jsonnet_evaluate_snippet_aux(JsonnetVm *vm, const char *filename,
                                          const char *snippet, int *error, bool multi)
{
    try {
        Allocator alloc;
        AST *expr = jsonnet_parse(&alloc, filename, snippet);
        std::string json_str;
        std::map<std::string, std::string> files;
        if (vm->debugAst) {
            json_str = jsonnet_unparse_jsonnet(expr);
        } else {
            jsonnet_static_analysis(expr);
            if (multi) {
                files = jsonnet_vm_execute_multi(&alloc, expr, vm->extVars, vm->maxStack,
                                                 vm->gcMinObjects, vm->gcGrowthTrigger,
                                                 vm->importCallback, vm->importCallbackContext,
                                                 vm->stringOutput);
            } else {
                json_str = jsonnet_vm_execute(&alloc, expr, vm->extVars, vm->maxStack,
                                              vm->gcMinObjects, vm->gcGrowthTrigger,
                                              vm->importCallback, vm->importCallbackContext,
                                              vm->stringOutput);
            }
        }
        if (multi) {
            size_t sz = 1; // final sentinel
            for (const auto &pair : files) {
                sz += pair.first.length() + 1; // include sentinel
                sz += pair.second.length() + 2; // Add a '\n' as well as sentinel
            }
            char *buf = (char*)::malloc(sz);
            if (buf == nullptr) memory_panic();
            std::ptrdiff_t i = 0;
            for (const auto &pair : files) {
                memcpy(&buf[i], pair.first.c_str(), pair.first.length() + 1);
                i += pair.first.length() + 1;
                memcpy(&buf[i], pair.second.c_str(), pair.second.length());
                i += pair.second.length();
                buf[i] = '\n';
                i++;
                buf[i] = '\0';
                i++;
            }
            buf[i] = '\0'; // final sentinel
            *error = false;
            return buf;
        } else {
            json_str += "\n";
            *error = false;
            return from_string(vm, json_str);
        }

    } catch (StaticError &e) {
        std::stringstream ss;
        ss << "STATIC ERROR: " << e << std::endl;
        *error = true;
        return from_string(vm, ss.str());

    } catch (RuntimeError &e) {
        std::stringstream ss;
        ss << "RUNTIME ERROR: " << e.msg << std::endl;
        const long max_above = vm->maxTrace / 2;
        const long max_below = vm->maxTrace - max_above;
        const long sz = e.stackTrace.size();
        for (long i = 0 ; i < sz ; ++i) {
            const auto &f = e.stackTrace[i];
            if (vm->maxTrace > 0 && i >= max_above && i < sz - max_below) {
                if (i == max_above)
                    ss << "\t..." << std::endl;
            } else {
                ss << "\t" << f.location << "\t" << f.name << std::endl;
            }
        }
        *error = true;
        return from_string(vm, ss.str());
    }

}

static char *jsonnet_evaluate_file_aux(JsonnetVm *vm, const char *filename, int *error, bool multi)
{
    std::ifstream f;
    f.open(filename);
    if (!f.good()) {
        std::stringstream ss;
        ss << "Opening input file: " << filename << ": " << strerror(errno);
        *error = true;
        return from_string(vm, ss.str());
    }
    std::string input;
    input.assign(std::istreambuf_iterator<char>(f),
                 std::istreambuf_iterator<char>());

    return jsonnet_evaluate_snippet_aux(vm, filename, input.c_str(), error, multi);
}

char *jsonnet_evaluate_file(JsonnetVm *vm, const char *filename, int *error)
{
    TRY
    return jsonnet_evaluate_file_aux(vm, filename, error, false);
    CATCH("jsonnet_evaluate_file")
    return nullptr;  // Never happens.
}

char *jsonnet_evaluate_file_multi(JsonnetVm *vm, const char *filename, int *error)
{
    TRY
    return jsonnet_evaluate_file_aux(vm, filename, error, true);
    CATCH("jsonnet_evaluate_file_multi")
    return nullptr;  // Never happens.
}

char *jsonnet_evaluate_snippet(JsonnetVm *vm, const char *filename, const char *snippet, int *error)
{
    TRY
    return jsonnet_evaluate_snippet_aux(vm, filename, snippet, error, false);
    CATCH("jsonnet_evaluate_snippet")
    return nullptr;  // Never happens.
}

char *jsonnet_evaluate_snippet_multi(JsonnetVm *vm, const char *filename,
                                     const char *snippet, int *error)
{
    TRY
    return jsonnet_evaluate_snippet_aux(vm, filename, snippet, error, true);
    CATCH("jsonnet_evaluate_snippet_multi")
    return nullptr;  // Never happens.
}

char *jsonnet_realloc(JsonnetVm *vm, char *str, size_t sz)
{
    (void) vm;
    if (str == nullptr) {
        if (sz == 0) return nullptr;
        auto *r = static_cast<char*>(::malloc(sz));
        if (r == nullptr) memory_panic();
        return r;
    } else {
        if (sz == 0) {
            ::free(str);
            return nullptr;
        } else {
            auto *r = static_cast<char*>(::realloc(str, sz));
            if (r == nullptr) memory_panic();
            return r;
        }
    }
}

<|endoftext|>