<commit_before>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: updatedispatch.cxx,v $
 *
 *  $Revision: 1.11 $
 *
 *  last change: $Author: ihi $ $Date: 2007-11-23 14:11:04 $
 *
 *  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
 *
 ************************************************************************/

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

#include "updatedispatch.hxx"

#ifndef CONFIGMGR_CONFIGPATH_HXX_
#include "configpath.hxx"
#endif
#ifndef INCLUDED_SHARABLE_NODE_HXX
#include "node.hxx"
#endif
#ifndef CONFIGMGR_TREEACCESSOR_HXX
#include "treeaccessor.hxx"
#endif
#ifndef CONFIGMGR_VALUENODEACCESS_HXX
#include "valuenodeaccess.hxx"
#endif
#ifndef CONFIGMGR_GROUPNODEACCESS_HXX
#include "groupnodeaccess.hxx"
#endif
#ifndef CONFIGMGR_SETNODEACCESS_HXX
#include "setnodeaccess.hxx"
#endif
#ifndef CONFIGMGR_MATCHLOCALE_HXX
#include "matchlocale.hxx"
#endif

#include <com/sun/star/configuration/backend/XUpdateHandler.hpp>
#include <com/sun/star/configuration/backend/NodeAttribute.hpp>

namespace configmgr
{
// -----------------------------------------------------------------------------
    namespace backend
    {
// -----------------------------------------------------------------------------

UpdateDispatcher::UpdateDispatcher(UpdateHandler const & _xUpdateHandler, OUString const & _aLocale)
: m_pContextPath(NULL)
, m_xUpdateHandler(_xUpdateHandler)
, m_aLocale(_aLocale)
, m_aElementName()
, m_bInValueSet(false)
, m_bInLocalizedValues(false)
{
}
// -----------------------------------------------------------------------------

UpdateDispatcher::~UpdateDispatcher()
{
}
// -----------------------------------------------------------------------------

void UpdateDispatcher::dispatchUpdate(configuration::AbsolutePath const & _aRootPath, SubtreeChange const& _anUpdate)
{
    if (!m_xUpdateHandler.is())
    {
        OUString sMsg( RTL_CONSTASCII_USTRINGPARAM("ERROR: Cannot dispatch update - no handler found") );
        throw uno::RuntimeException(sMsg,NULL);
    }

    OSL_PRECOND( !_aRootPath.isRoot(), "Cannot apply update, where root is outside a component" );

    OSL_PRECOND( m_pContextPath == NULL, "Update Dispatcher already has a context path" );
    if (!_aRootPath.getParentPath().isRoot())
    {
        OSL_ENSURE(false,"Obsolete functionality used: starting update with non-empty context");
        m_pContextPath = &_aRootPath;
    }

    this->startUpdate();
    this->applyToChange(_anUpdate);
    this->endUpdate();

    m_pContextPath = NULL;
}
// -----------------------------------------------------------------------------

void UpdateDispatcher::startUpdate()
{
    m_xUpdateHandler->startUpdate();
    m_bInValueSet = false;
    m_bInLocalizedValues = false;
    m_aElementName = OUString();

    if (m_pContextPath)
    {
        configuration::AbsolutePath::Iterator   it = m_pContextPath->begin();
        configuration::AbsolutePath::Iterator   stop = m_pContextPath->end();

        OSL_ASSERT(it != stop);
        --stop;

        for ( ; it != stop; ++it)
        {
            m_xUpdateHandler->modifyNode(it->getName().toString(),0,0,false);
        }
    }
}
// -----------------------------------------------------------------------------

void UpdateDispatcher::endUpdate()
{
    if (m_pContextPath)
    {
        configuration::AbsolutePath::Iterator   it = m_pContextPath->begin();
        configuration::AbsolutePath::Iterator   stop = m_pContextPath->end();

        OSL_ASSERT(it != stop);
        --stop;

        for ( ; it != stop; ++it)
        {
            m_xUpdateHandler->endNode();
        }
    }
    m_xUpdateHandler->endUpdate();
}
// -----------------------------------------------------------------------------

void UpdateDispatcher::handle(ValueChange const& aValueNode)
{
    // special case: doing members of a localized property (as set)
    if (m_bInLocalizedValues)
    {
        OUString aLocale = aValueNode.getNodeName();

        if (aLocale.getLength())
        {
            if ( aValueNode.isToDefault() )
                m_xUpdateHandler->resetPropertyValueForLocale( aLocale );
             else
                m_xUpdateHandler->setPropertyValueForLocale( aValueNode.getNewValue(), aLocale );
        }
        else
        {
            if ( aValueNode.isToDefault() )
                m_xUpdateHandler->resetPropertyValue( );
             else
                m_xUpdateHandler->setPropertyValue( aValueNode.getNewValue() );
        }
        return;
    }

    // normal case: updating a single property
    switch (aValueNode.getMode())
    {
    case ValueChange::wasDefault:
        if (aValueNode.isReplacedValue())
        {

            OSL_ENSURE(m_bInValueSet, "UpdateDispatcher: Cannot add/replace a value in a nonextensible node");
            OSL_ENSURE(!aValueNode.isLocalizedValue(), "UpdateDispatcher: Cannot add a localized value in a layer");

            sal_Int16 nAttr = getUpdateAttributes(aValueNode.getAttributes(),true);

            if (aValueNode.getNewValue().hasValue())
            {
                m_xUpdateHandler->addOrReplacePropertyWithValue( aValueNode.getNodeName(),
                                                                 nAttr,
                                                                 aValueNode.getNewValue());
            }
            else
            {
                m_xUpdateHandler->addOrReplaceProperty( aValueNode.getNodeName(),
                                                        nAttr,
                                                        aValueNode.getValueType());
            }

            break;
        }
        // else fall thru to changeValue case

    case ValueChange::changeValue:
        {
            sal_Int16 nAttr     = getUpdateAttributes(aValueNode.getAttributes(),false);
            sal_Int16 nAttrMask = getUpdateAttributeMask(aValueNode.getAttributes());

            m_xUpdateHandler->modifyProperty( aValueNode.getNodeName(),
                                                nAttr, nAttrMask,
                                              aValueNode.getValueType() );

            if (aValueNode.isLocalizedValue() && m_aLocale.getLength())
            {
                m_xUpdateHandler->setPropertyValueForLocale( aValueNode.getNewValue(), m_aLocale );
            }
            else
            {
                m_xUpdateHandler->setPropertyValue( aValueNode.getNewValue() );
            }

            m_xUpdateHandler->endProperty();
        }
        break;

    case ValueChange::setToDefault:
        m_xUpdateHandler->resetProperty( aValueNode.getNodeName() );
        break;

    case ValueChange::changeDefault:
        OSL_ENSURE(false, "Illegal mode in ValueChange");
        break;

    default:
        OSL_ENSURE(false, "Illegal mode in ValueChange");
        break;
    };
}
// -----------------------------------------------------------------------------

void UpdateDispatcher::handle(AddNode const& aAddNode)
{
    data::TreeSegment aAddedTree = aAddNode.getNewTree();

    OSL_ENSURE(aAddedTree.is(), "AddNode has no new data -> cannot add anything");

    OSL_ENSURE( !aAddedTree.is() ||
                ((m_bInValueSet||m_bInLocalizedValues) == aAddedTree.getSegmentRootNode()->isValue()),
                "Found added subtree in value set (extensible group)\n" );

    this->visitTree( aAddedTree.getTreeAccess() );
}
// -----------------------------------------------------------------------------

void UpdateDispatcher::handle(RemoveNode const& aRemoveNode)
{
    OSL_ENSURE( !m_bInLocalizedValues, "UpdateDispatcher: Removing values for a specific locale is currently not supported");

    data::TreeSegment aRemovedTree = aRemoveNode.getRemovedTree();

    OSL_ENSURE( !aRemovedTree.is() ||
                ((m_bInValueSet||m_bInLocalizedValues) == aRemovedTree.getSegmentRootNode()->isValue()),
                "Found removed subtree in value set (extensible group)\n" );

    if (m_bInLocalizedValues)
        OSL_TRACE("configmgr: UpdateDispatcher - Removing value for locale ignored");

    else if (m_bInValueSet)
        m_xUpdateHandler->removeProperty( aRemoveNode.getNodeName() );

    else
        m_xUpdateHandler->removeNode( aRemoveNode.getNodeName() );
}
// -----------------------------------------------------------------------------

void UpdateDispatcher::handle(SubtreeChange const& aSubtree)
{
    OSL_ENSURE( !m_bInLocalizedValues, "UpdateDispatcher: A localized value cannot be a complete subtree");
    OSL_ENSURE( !m_bInValueSet, "UpdateDispatcher: A dynamic property cannot be a complete subtree");

    sal_Int16 nAttr     = getUpdateAttributes(aSubtree.getAttributes(),false);
    sal_Int16 nAttrMask = getUpdateAttributeMask(aSubtree.getAttributes());

    if (isLocalizedValueSet(aSubtree))
    {
        m_xUpdateHandler->modifyProperty( aSubtree.getNodeName(),
                                            nAttr, nAttrMask,
                                            uno::Type() );

        m_bInLocalizedValues = true;
        this->applyToChildren(aSubtree);
        m_bInLocalizedValues = false;

        m_xUpdateHandler->endProperty();
    }

    else
    {
        m_xUpdateHandler->modifyNode( aSubtree.getNodeName(),
                                       nAttr, nAttrMask,
                                      aSubtree.isToDefault() );

        m_bInValueSet = isValueSet(aSubtree);
        this->applyToChildren(aSubtree);
        m_bInValueSet = false;

        m_xUpdateHandler->endNode();
    }
}
// -----------------------------------------------------------------------------

data::SetVisitor::Result UpdateDispatcher::handle(data::ValueNodeAccess const& _aNode)
{
    OUString aName;

    // special case: doing members of a localized property (as set)
    if (m_bInLocalizedValues)
    {
        // the node name is the locale
        OUString aLocale;
        OSL_VERIFY(testReplacedAndGetName(_aNode,aLocale)); // "Adding a localized subvalue but not as root of element tree"

        if (aLocale.getLength() && ! localehelper::isDefaultLanguage(aLocale))
        {
            m_xUpdateHandler->setPropertyValueForLocale( _aNode.getValue(), aLocale );
        }
        else
        {
            m_xUpdateHandler->setPropertyValue( _aNode.getValue() );
        }
    }
    else if (testReplacedAndGetName(_aNode,aName)&& (_aNode.getAttributes().isRemovable()) ) // we must be inside a set of values
    {

        OSL_ENSURE(!_aNode.isLocalized(), "UpdateDispatcher: Cannot add a localized value in a layer .");

        sal_Int16 nAttr = getUpdateAttributes(_aNode.getAttributes(),true);

        if (!_aNode.isNull())
        {
            m_xUpdateHandler->addOrReplacePropertyWithValue( aName,
                                                             nAttr,
                                                             _aNode.getValue());
        }
        else
        {
            m_xUpdateHandler->addOrReplaceProperty( aName,
                                                    nAttr,
                                                    _aNode.getValueType());
        }
    }
    else // normal case: updating a single property //Inserting set
    {
         sal_Int16 nAttr     = getUpdateAttributes(_aNode.getAttributes(),false);
        sal_Int16 nAttrMask = getUpdateAttributeMask(_aNode.getAttributes());

        m_xUpdateHandler->modifyProperty( aName, nAttr, nAttrMask, _aNode.getValueType() );

        if (_aNode.isLocalized() && m_aLocale.getLength())
        {
            m_xUpdateHandler->setPropertyValueForLocale( _aNode.getValue(), m_aLocale );
        }
        else
        {
            m_xUpdateHandler->setPropertyValue( _aNode.getValue() );
        }

        m_xUpdateHandler->endProperty();
    }
    return CONTINUE;
}
// -----------------------------------------------------------------------------

data::SetVisitor::Result UpdateDispatcher::handle(data::GroupNodeAccess const& _aNode)
{
    OSL_ENSURE( !m_bInLocalizedValues, "UpdateDispatcher: A localized value cannot be a complete group");

    OUString aName;

    if ( testReplacedAndGetName(_aNode,aName) )
    {
        sal_Int16 nAttr = getUpdateAttributes(_aNode.getAttributes(),true);

        m_xUpdateHandler->addOrReplaceNode( aName, nAttr );

        this->visitChildren(_aNode);

        m_xUpdateHandler->endNode();
    }
    else
    {
        sal_Int16 nAttr     = getUpdateAttributes(_aNode.getAttributes(),false);
        sal_Int16 nAttrMask = getUpdateAttributeMask(_aNode.getAttributes());

        m_xUpdateHandler->modifyNode( aName, nAttr, nAttrMask, false );

        this->visitChildren(_aNode);

        m_xUpdateHandler->endNode();
    }
    return CONTINUE;
}
// -----------------------------------------------------------------------------

data::SetVisitor::Result UpdateDispatcher::handle(data::SetNodeAccess const& _aNode)
{
    OSL_ENSURE( !m_bInLocalizedValues, "UpdateDispatcher: A localized value cannot be a complete set");

    OUString aName;

    if ( testReplacedAndGetName(_aNode,aName) )
    {
        OSL_ENSURE( !_aNode.isLocalizedValueSetNode(), "UpdateDispatcher: Cannot add a localized value in a layer." );

        sal_Int16 nAttr     = getUpdateAttributes(_aNode.getAttributes(),true);

        m_xUpdateHandler->addOrReplaceNode( aName, nAttr );

        this->visitElements(_aNode);

        m_xUpdateHandler->endNode();
    }
    else
    {
        sal_Int16 nAttr     = getUpdateAttributes(_aNode.getAttributes(),false);
        sal_Int16 nAttrMask = getUpdateAttributeMask(_aNode.getAttributes());

        if (_aNode.isLocalizedValueSetNode())
        {
            m_xUpdateHandler->modifyProperty( aName, nAttr, nAttrMask, uno::Type() );

            m_bInLocalizedValues = true;
            this->visitElements(_aNode);
            m_bInLocalizedValues = false;

            m_xUpdateHandler->endProperty();
        }

        else
        {
            m_xUpdateHandler->modifyNode( aName, nAttr, nAttrMask, false );

            this->visitElements(_aNode);

            m_xUpdateHandler->endNode();
        }
    }
    return CONTINUE;
}
// -----------------------------------------------------------------------------

bool UpdateDispatcher::testReplacedAndGetName(data::NodeAccess const & _aNode, OUString & _aName)
{
    if (m_aElementName.getLength())
    {
        OSL_ENSURE( _aNode->isFragmentRoot(), "ERROR - UpdateDispatcher: Found orphaned 'element' name for inner node");
        _aName = m_aElementName;
        m_aElementName = OUString();
        return true;
    }
    else
    {
        OSL_ENSURE(!_aNode->isFragmentRoot(), "ERROR - UpdateDispatcher: Found no 'element' name for fragment root node");
        _aName = _aNode.getName().toString();
        return false;
    }
}
// -----------------------------------------------------------------------------

data::SetVisitor::Result UpdateDispatcher::handle(data::TreeAccessor const& _aElement)
{
    m_aElementName = _aElement.getName().toString();
    Result aRes = SetVisitor::handle(_aElement); // dispatch to root node
    m_aElementName = OUString(); // clear - just to be safe
    return aRes;
}
// -----------------------------------------------------------------------------

sal_Int16 UpdateDispatcher::getUpdateAttributes(node::Attributes const & _aAttributes, bool bAdded)
{
    namespace NodeAttribute = backenduno::NodeAttribute;

    // no support for post-creation attribute changes yet
    if (!bAdded)
    {
        OSL_ENSURE( getUpdateAttributeMask(_aAttributes) == 0,
                    "Incomplete support for attribute changes" );
        return 0;
    }

    sal_Int16 nResult = 0;

    if (_aAttributes.isReadonly())
        nResult = NodeAttribute::READONLY;

    if (_aAttributes.isFinalized())
        nResult |= NodeAttribute::FINALIZED;

    if (!_aAttributes.isNullable())
        nResult |= NodeAttribute::MANDATORY;

    return nResult;
}
// -----------------------------------------------------------------------------

sal_Int16 UpdateDispatcher::getUpdateAttributeMask(node::Attributes const & /*_aAttributes*/)
{
    // no support for post-creation attribute changes yet
    return 0;
}
// -----------------------------------------------------------------------------

// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
    } // namespace backend

// -------------------------------------------------------------------------
} // namespace configmgr
<commit_msg>INTEGRATION: CWS changefileheader (1.11.16); FILE MERGED 2008/04/01 12:27:19 thb 1.11.16.2: #i85898# Stripping all external header guards 2008/03/31 12:22:41 rt 1.11.16.1: #i87441# Change license header to LPGL v3.<commit_after>/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * Copyright 2008 by Sun Microsystems, Inc.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * $RCSfile: updatedispatch.cxx,v $
 * $Revision: 1.12 $
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/

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

#include "updatedispatch.hxx"
#include "configpath.hxx"
#include "node.hxx"
#include "treeaccessor.hxx"
#include "valuenodeaccess.hxx"
#include "groupnodeaccess.hxx"
#include "setnodeaccess.hxx"
#include "matchlocale.hxx"

#include <com/sun/star/configuration/backend/XUpdateHandler.hpp>
#include <com/sun/star/configuration/backend/NodeAttribute.hpp>

namespace configmgr
{
// -----------------------------------------------------------------------------
    namespace backend
    {
// -----------------------------------------------------------------------------

UpdateDispatcher::UpdateDispatcher(UpdateHandler const & _xUpdateHandler, OUString const & _aLocale)
: m_pContextPath(NULL)
, m_xUpdateHandler(_xUpdateHandler)
, m_aLocale(_aLocale)
, m_aElementName()
, m_bInValueSet(false)
, m_bInLocalizedValues(false)
{
}
// -----------------------------------------------------------------------------

UpdateDispatcher::~UpdateDispatcher()
{
}
// -----------------------------------------------------------------------------

void UpdateDispatcher::dispatchUpdate(configuration::AbsolutePath const & _aRootPath, SubtreeChange const& _anUpdate)
{
    if (!m_xUpdateHandler.is())
    {
        OUString sMsg( RTL_CONSTASCII_USTRINGPARAM("ERROR: Cannot dispatch update - no handler found") );
        throw uno::RuntimeException(sMsg,NULL);
    }

    OSL_PRECOND( !_aRootPath.isRoot(), "Cannot apply update, where root is outside a component" );

    OSL_PRECOND( m_pContextPath == NULL, "Update Dispatcher already has a context path" );
    if (!_aRootPath.getParentPath().isRoot())
    {
        OSL_ENSURE(false,"Obsolete functionality used: starting update with non-empty context");
        m_pContextPath = &_aRootPath;
    }

    this->startUpdate();
    this->applyToChange(_anUpdate);
    this->endUpdate();

    m_pContextPath = NULL;
}
// -----------------------------------------------------------------------------

void UpdateDispatcher::startUpdate()
{
    m_xUpdateHandler->startUpdate();
    m_bInValueSet = false;
    m_bInLocalizedValues = false;
    m_aElementName = OUString();

    if (m_pContextPath)
    {
        configuration::AbsolutePath::Iterator   it = m_pContextPath->begin();
        configuration::AbsolutePath::Iterator   stop = m_pContextPath->end();

        OSL_ASSERT(it != stop);
        --stop;

        for ( ; it != stop; ++it)
        {
            m_xUpdateHandler->modifyNode(it->getName().toString(),0,0,false);
        }
    }
}
// -----------------------------------------------------------------------------

void UpdateDispatcher::endUpdate()
{
    if (m_pContextPath)
    {
        configuration::AbsolutePath::Iterator   it = m_pContextPath->begin();
        configuration::AbsolutePath::Iterator   stop = m_pContextPath->end();

        OSL_ASSERT(it != stop);
        --stop;

        for ( ; it != stop; ++it)
        {
            m_xUpdateHandler->endNode();
        }
    }
    m_xUpdateHandler->endUpdate();
}
// -----------------------------------------------------------------------------

void UpdateDispatcher::handle(ValueChange const& aValueNode)
{
    // special case: doing members of a localized property (as set)
    if (m_bInLocalizedValues)
    {
        OUString aLocale = aValueNode.getNodeName();

        if (aLocale.getLength())
        {
            if ( aValueNode.isToDefault() )
                m_xUpdateHandler->resetPropertyValueForLocale( aLocale );
             else
                m_xUpdateHandler->setPropertyValueForLocale( aValueNode.getNewValue(), aLocale );
        }
        else
        {
            if ( aValueNode.isToDefault() )
                m_xUpdateHandler->resetPropertyValue( );
             else
                m_xUpdateHandler->setPropertyValue( aValueNode.getNewValue() );
        }
        return;
    }

    // normal case: updating a single property
    switch (aValueNode.getMode())
    {
    case ValueChange::wasDefault:
        if (aValueNode.isReplacedValue())
        {

            OSL_ENSURE(m_bInValueSet, "UpdateDispatcher: Cannot add/replace a value in a nonextensible node");
            OSL_ENSURE(!aValueNode.isLocalizedValue(), "UpdateDispatcher: Cannot add a localized value in a layer");

            sal_Int16 nAttr = getUpdateAttributes(aValueNode.getAttributes(),true);

            if (aValueNode.getNewValue().hasValue())
            {
                m_xUpdateHandler->addOrReplacePropertyWithValue( aValueNode.getNodeName(),
                                                                 nAttr,
                                                                 aValueNode.getNewValue());
            }
            else
            {
                m_xUpdateHandler->addOrReplaceProperty( aValueNode.getNodeName(),
                                                        nAttr,
                                                        aValueNode.getValueType());
            }

            break;
        }
        // else fall thru to changeValue case

    case ValueChange::changeValue:
        {
            sal_Int16 nAttr     = getUpdateAttributes(aValueNode.getAttributes(),false);
            sal_Int16 nAttrMask = getUpdateAttributeMask(aValueNode.getAttributes());

            m_xUpdateHandler->modifyProperty( aValueNode.getNodeName(),
                                                nAttr, nAttrMask,
                                              aValueNode.getValueType() );

            if (aValueNode.isLocalizedValue() && m_aLocale.getLength())
            {
                m_xUpdateHandler->setPropertyValueForLocale( aValueNode.getNewValue(), m_aLocale );
            }
            else
            {
                m_xUpdateHandler->setPropertyValue( aValueNode.getNewValue() );
            }

            m_xUpdateHandler->endProperty();
        }
        break;

    case ValueChange::setToDefault:
        m_xUpdateHandler->resetProperty( aValueNode.getNodeName() );
        break;

    case ValueChange::changeDefault:
        OSL_ENSURE(false, "Illegal mode in ValueChange");
        break;

    default:
        OSL_ENSURE(false, "Illegal mode in ValueChange");
        break;
    };
}
// -----------------------------------------------------------------------------

void UpdateDispatcher::handle(AddNode const& aAddNode)
{
    data::TreeSegment aAddedTree = aAddNode.getNewTree();

    OSL_ENSURE(aAddedTree.is(), "AddNode has no new data -> cannot add anything");

    OSL_ENSURE( !aAddedTree.is() ||
                ((m_bInValueSet||m_bInLocalizedValues) == aAddedTree.getSegmentRootNode()->isValue()),
                "Found added subtree in value set (extensible group)\n" );

    this->visitTree( aAddedTree.getTreeAccess() );
}
// -----------------------------------------------------------------------------

void UpdateDispatcher::handle(RemoveNode const& aRemoveNode)
{
    OSL_ENSURE( !m_bInLocalizedValues, "UpdateDispatcher: Removing values for a specific locale is currently not supported");

    data::TreeSegment aRemovedTree = aRemoveNode.getRemovedTree();

    OSL_ENSURE( !aRemovedTree.is() ||
                ((m_bInValueSet||m_bInLocalizedValues) == aRemovedTree.getSegmentRootNode()->isValue()),
                "Found removed subtree in value set (extensible group)\n" );

    if (m_bInLocalizedValues)
        OSL_TRACE("configmgr: UpdateDispatcher - Removing value for locale ignored");

    else if (m_bInValueSet)
        m_xUpdateHandler->removeProperty( aRemoveNode.getNodeName() );

    else
        m_xUpdateHandler->removeNode( aRemoveNode.getNodeName() );
}
// -----------------------------------------------------------------------------

void UpdateDispatcher::handle(SubtreeChange const& aSubtree)
{
    OSL_ENSURE( !m_bInLocalizedValues, "UpdateDispatcher: A localized value cannot be a complete subtree");
    OSL_ENSURE( !m_bInValueSet, "UpdateDispatcher: A dynamic property cannot be a complete subtree");

    sal_Int16 nAttr     = getUpdateAttributes(aSubtree.getAttributes(),false);
    sal_Int16 nAttrMask = getUpdateAttributeMask(aSubtree.getAttributes());

    if (isLocalizedValueSet(aSubtree))
    {
        m_xUpdateHandler->modifyProperty( aSubtree.getNodeName(),
                                            nAttr, nAttrMask,
                                            uno::Type() );

        m_bInLocalizedValues = true;
        this->applyToChildren(aSubtree);
        m_bInLocalizedValues = false;

        m_xUpdateHandler->endProperty();
    }

    else
    {
        m_xUpdateHandler->modifyNode( aSubtree.getNodeName(),
                                       nAttr, nAttrMask,
                                      aSubtree.isToDefault() );

        m_bInValueSet = isValueSet(aSubtree);
        this->applyToChildren(aSubtree);
        m_bInValueSet = false;

        m_xUpdateHandler->endNode();
    }
}
// -----------------------------------------------------------------------------

data::SetVisitor::Result UpdateDispatcher::handle(data::ValueNodeAccess const& _aNode)
{
    OUString aName;

    // special case: doing members of a localized property (as set)
    if (m_bInLocalizedValues)
    {
        // the node name is the locale
        OUString aLocale;
        OSL_VERIFY(testReplacedAndGetName(_aNode,aLocale)); // "Adding a localized subvalue but not as root of element tree"

        if (aLocale.getLength() && ! localehelper::isDefaultLanguage(aLocale))
        {
            m_xUpdateHandler->setPropertyValueForLocale( _aNode.getValue(), aLocale );
        }
        else
        {
            m_xUpdateHandler->setPropertyValue( _aNode.getValue() );
        }
    }
    else if (testReplacedAndGetName(_aNode,aName)&& (_aNode.getAttributes().isRemovable()) ) // we must be inside a set of values
    {

        OSL_ENSURE(!_aNode.isLocalized(), "UpdateDispatcher: Cannot add a localized value in a layer .");

        sal_Int16 nAttr = getUpdateAttributes(_aNode.getAttributes(),true);

        if (!_aNode.isNull())
        {
            m_xUpdateHandler->addOrReplacePropertyWithValue( aName,
                                                             nAttr,
                                                             _aNode.getValue());
        }
        else
        {
            m_xUpdateHandler->addOrReplaceProperty( aName,
                                                    nAttr,
                                                    _aNode.getValueType());
        }
    }
    else // normal case: updating a single property //Inserting set
    {
         sal_Int16 nAttr     = getUpdateAttributes(_aNode.getAttributes(),false);
        sal_Int16 nAttrMask = getUpdateAttributeMask(_aNode.getAttributes());

        m_xUpdateHandler->modifyProperty( aName, nAttr, nAttrMask, _aNode.getValueType() );

        if (_aNode.isLocalized() && m_aLocale.getLength())
        {
            m_xUpdateHandler->setPropertyValueForLocale( _aNode.getValue(), m_aLocale );
        }
        else
        {
            m_xUpdateHandler->setPropertyValue( _aNode.getValue() );
        }

        m_xUpdateHandler->endProperty();
    }
    return CONTINUE;
}
// -----------------------------------------------------------------------------

data::SetVisitor::Result UpdateDispatcher::handle(data::GroupNodeAccess const& _aNode)
{
    OSL_ENSURE( !m_bInLocalizedValues, "UpdateDispatcher: A localized value cannot be a complete group");

    OUString aName;

    if ( testReplacedAndGetName(_aNode,aName) )
    {
        sal_Int16 nAttr = getUpdateAttributes(_aNode.getAttributes(),true);

        m_xUpdateHandler->addOrReplaceNode( aName, nAttr );

        this->visitChildren(_aNode);

        m_xUpdateHandler->endNode();
    }
    else
    {
        sal_Int16 nAttr     = getUpdateAttributes(_aNode.getAttributes(),false);
        sal_Int16 nAttrMask = getUpdateAttributeMask(_aNode.getAttributes());

        m_xUpdateHandler->modifyNode( aName, nAttr, nAttrMask, false );

        this->visitChildren(_aNode);

        m_xUpdateHandler->endNode();
    }
    return CONTINUE;
}
// -----------------------------------------------------------------------------

data::SetVisitor::Result UpdateDispatcher::handle(data::SetNodeAccess const& _aNode)
{
    OSL_ENSURE( !m_bInLocalizedValues, "UpdateDispatcher: A localized value cannot be a complete set");

    OUString aName;

    if ( testReplacedAndGetName(_aNode,aName) )
    {
        OSL_ENSURE( !_aNode.isLocalizedValueSetNode(), "UpdateDispatcher: Cannot add a localized value in a layer." );

        sal_Int16 nAttr     = getUpdateAttributes(_aNode.getAttributes(),true);

        m_xUpdateHandler->addOrReplaceNode( aName, nAttr );

        this->visitElements(_aNode);

        m_xUpdateHandler->endNode();
    }
    else
    {
        sal_Int16 nAttr     = getUpdateAttributes(_aNode.getAttributes(),false);
        sal_Int16 nAttrMask = getUpdateAttributeMask(_aNode.getAttributes());

        if (_aNode.isLocalizedValueSetNode())
        {
            m_xUpdateHandler->modifyProperty( aName, nAttr, nAttrMask, uno::Type() );

            m_bInLocalizedValues = true;
            this->visitElements(_aNode);
            m_bInLocalizedValues = false;

            m_xUpdateHandler->endProperty();
        }

        else
        {
            m_xUpdateHandler->modifyNode( aName, nAttr, nAttrMask, false );

            this->visitElements(_aNode);

            m_xUpdateHandler->endNode();
        }
    }
    return CONTINUE;
}
// -----------------------------------------------------------------------------

bool UpdateDispatcher::testReplacedAndGetName(data::NodeAccess const & _aNode, OUString & _aName)
{
    if (m_aElementName.getLength())
    {
        OSL_ENSURE( _aNode->isFragmentRoot(), "ERROR - UpdateDispatcher: Found orphaned 'element' name for inner node");
        _aName = m_aElementName;
        m_aElementName = OUString();
        return true;
    }
    else
    {
        OSL_ENSURE(!_aNode->isFragmentRoot(), "ERROR - UpdateDispatcher: Found no 'element' name for fragment root node");
        _aName = _aNode.getName().toString();
        return false;
    }
}
// -----------------------------------------------------------------------------

data::SetVisitor::Result UpdateDispatcher::handle(data::TreeAccessor const& _aElement)
{
    m_aElementName = _aElement.getName().toString();
    Result aRes = SetVisitor::handle(_aElement); // dispatch to root node
    m_aElementName = OUString(); // clear - just to be safe
    return aRes;
}
// -----------------------------------------------------------------------------

sal_Int16 UpdateDispatcher::getUpdateAttributes(node::Attributes const & _aAttributes, bool bAdded)
{
    namespace NodeAttribute = backenduno::NodeAttribute;

    // no support for post-creation attribute changes yet
    if (!bAdded)
    {
        OSL_ENSURE( getUpdateAttributeMask(_aAttributes) == 0,
                    "Incomplete support for attribute changes" );
        return 0;
    }

    sal_Int16 nResult = 0;

    if (_aAttributes.isReadonly())
        nResult = NodeAttribute::READONLY;

    if (_aAttributes.isFinalized())
        nResult |= NodeAttribute::FINALIZED;

    if (!_aAttributes.isNullable())
        nResult |= NodeAttribute::MANDATORY;

    return nResult;
}
// -----------------------------------------------------------------------------

sal_Int16 UpdateDispatcher::getUpdateAttributeMask(node::Attributes const & /*_aAttributes*/)
{
    // no support for post-creation attribute changes yet
    return 0;
}
// -----------------------------------------------------------------------------

// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
    } // namespace backend

// -------------------------------------------------------------------------
} // namespace configmgr
<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  $RCSfile: disposetimer.cxx,v $
 *
 *  $Revision: 1.17 $
 *
 *  last change: $Author: jb $ $Date: 2002-03-28 09:08:05 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 by Sun Microsystems, Inc.
 *  901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License version 2.1, as published by the Free Software Foundation.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *  MA  02111-1307  USA
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (the "License"); You may not use this file
 *  except in compliance with the License. You may obtain a copy of the
 *  License at http://www.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 *
 *  Copyright: 2000 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): _______________________________________
 *
 *
 ************************************************************************/

#include <stdio.h>

#include "disposetimer.hxx"

#ifndef CONFIGMGR_BACKEND_CACHECONTROLLER_HXX
#include "cachecontroller.hxx"
#endif

#ifndef CONFIGMGR_CONFIGEXCEPT_HXX_
#include "configexcept.hxx"
#endif

#ifndef _CONFIGMGR_TRACER_HXX_
#include "tracer.hxx"
#endif

#ifndef _OSL_DIAGNOSE_H_
#include <osl/diagnose.h>
#endif

namespace configmgr
{
//==========================================================================
//=
//==========================================================================

void OTreeDisposeScheduler::scheduleCleanup(RequestOptions const& _aOptions)
{
    OSL_ENSURE(_aOptions.hasLocale(), "ERROR: OTreeDisposeScheduler: cannot handle complete user scheduling");

    osl::MutexGuard aGuard( m_aMutex );

    CFG_TRACE_INFO("Scheduling data cleanup for user '%s' with locale '%s'",
                    OUSTRING2ASCII(_aOptions.getEntity()),
                    OUSTRING2ASCII(_aOptions.getLocale()));

    CFG_TRACE_INFO_NI("- Cleanup will be started in about %d seconds", int(m_aCleanupDelay.getTimeValue().Seconds));

    TimeStamp aNewTime = implGetCleanupTime(TimeStamp::getCurrentTime(), m_aCleanupDelay);
    OSL_ASSERT(!aNewTime.isNever());

    TimeStamp aScheduleTime = implAddTask(_aOptions, aNewTime);

    OSL_ASSERT(aScheduleTime <= aNewTime);
    OSL_ASSERT(!aScheduleTime.isNever());

    implStartBefore(aNewTime);
}
// -------------------------------------------------------------------------

static
inline
bool equivalentOptions(RequestOptions const& lhs, RequestOptions const& rhs)
{
    lessRequestOptions lessThan;
    return ! (lessThan(lhs,rhs) || lessThan(rhs,lhs));
}
// -------------------------------------------------------------------------

void OTreeDisposeScheduler::clearTasks(RequestOptions const& _aOptions)
{
    osl::MutexGuard aOwnGuard( m_aMutex );

    CFG_TRACE_INFO("Cancelling all data cleanup tasks for user '%s' with locale '%s'",
                    OUSTRING2ASCII(_aOptions.getEntity()),
                    OUSTRING2ASCII(_aOptions.getLocale()));

    Agenda::iterator it = m_aAgenda.begin();
    while(it != m_aAgenda.end())
    {
        Agenda::iterator cur = it++;
        if (equivalentOptions(_aOptions,cur->second))
        {
            m_aAgenda.erase(cur);
            CFG_TRACE_INFO_NI("- One pending task canceled");
        }
    }
}
// -------------------------------------------------------------------------

void OTreeDisposeScheduler::stopAndClearTasks()
{
    osl::MutexGuard aOwnGuard( m_aMutex );

    CFG_TRACE_INFO("Cancelling all data cleanup tasks, Stopping Cleanup timer");
    CFG_TRACE_INFO_NI("- %d cleanup tasks were pending", int(m_aAgenda.size()) );

    if (m_xTimer.isValid())
        m_xTimer->dispose(); // just to be sure

    m_aAgenda.clear();
}
// -------------------------------------------------------------------------

OTreeDisposeScheduler::Task OTreeDisposeScheduler::getTask(TimeStamp const& _aActualTime, TimeStamp& _rNextTime)
{
    OSL_ASSERT( _rNextTime.isNever() ); // internal contract, we set this only in the positive case

    osl::MutexGuard aOwnGuard( m_aMutex );
    Task aTask( false, RequestOptions() );

    if (!m_aAgenda.empty())
    {
        Agenda::iterator const it = m_aAgenda.begin();

        if (it->first <= _aActualTime)
        {
            aTask = std::make_pair(true,it->second);
            m_aAgenda.erase(it);
        }
    }

    if (!m_aAgenda.empty())
    {
        Agenda::iterator const it = m_aAgenda.begin();

        _rNextTime = it->first;
    }

    return aTask;
}
// -------------------------------------------------------------------------

void OTreeDisposeScheduler::Timer::onShot()
{
    osl::MutexGuard aGuard(m_aMutex);
    if (pParent)
        pParent->onTimerShot();
}
// -------------------------------------------------------------------------

void OTreeDisposeScheduler::onTimerShot()
{
    CFG_TRACE_INFO("Cleanup Timer invoked - executing dispose task");

    TimeStamp aActualTime = TimeStamp::getCurrentTime();
    TimeStamp aNextTime = implGetCleanupTime(aActualTime, getCleanupInterval());

    try
    {
        TimeStamp aNextDisposeTime = runDisposer(aActualTime);

        if (aNextTime < aNextDisposeTime)
            aNextTime = aNextDisposeTime;
    }

    catch (uno::Exception& ue)
    {
        OSL_ENSURE(false, "ERROR: UNO Exception left a disposer");
        ue;
    }
    catch (configuration::Exception& ce)
    {
        OSL_ENSURE(false, "ERROR: configuration::Exception left a disposer");
        ce;
    }
    catch (...)
    {
        OSL_ENSURE(false, "ERROR: Unknown Exception left a disposer");
    }

    osl::MutexGuard aGuard(m_aMutex);
    implStartBefore(aNextTime);
}
// -------------------------------------------------------------------------

// this really should be a member of the TreeManager (see TreeManager::disposeOne etc.)
TimeStamp OTreeDisposeScheduler::runDisposer(TimeStamp const& _aActualTime)
{
    TimeStamp aNextTime = TimeStamp::never();
    OSL_ASSERT(aNextTime.isNever());

    osl::ClearableMutexGuard aGuard( m_rTreeManager.m_aCacheList.mutex() );

    Task aTask = this->getTask( _aActualTime, aNextTime );
    if (aTask.first)
    {
        RequestOptions & rTaskOptions = aTask.second;

        CFG_TRACE_INFO("Found cleanup task for user %s and locale %s",
                        OUSTRING2ASCII(rTaskOptions.getEntity()),
                        OUSTRING2ASCII(rTaskOptions.getLocale()));

        CacheManager::CacheRef aCache = m_rTreeManager.m_aCacheList.get(rTaskOptions);
        if (aCache.is())
        {
            CFG_TRACE_INFO_NI("- Found matching data container (TreeInfo) - collecting data");

            CacheLoadingAccess::DisposeList aDisposeList;

            TimeStamp aNextTaskTime = aCache->collectDisposeList(aDisposeList, _aActualTime, m_aCleanupDelay);

            CFG_TRACE_INFO_NI("- Found %d module trees to dispose", int(aDisposeList.size()) );

            if (!aNextTaskTime.isNever())
            {
                OSL_ENSURE( !aCache->isEmpty(), "ERROR: Empty TreeInfo returning finite dispose time");

                // repost with new time
                osl::MutexGuard aOwnGuard( m_aMutex );

                CFG_TRACE_INFO_NI("- Rescheduling current option set" );

                aNextTime = this->implAddTask(rTaskOptions,aNextTaskTime);
            }

            else if (aCache->isEmpty())// may have been the last one - check that
            {
                // currently it is not possible to release options which are
                // because it is not save to delete the info if another thread is running in
                // a read request

                /*

                CFG_TRACE_INFO_NI("- Disposing last data for this options set => Removing TreeInfo" );

                // get rid of it - see TreeManager::disposeOne
                std::auto_ptr<TreeInfo> pDisposeInfo(pInfo);

                m_rTreeManager.m_aTreeList.erase(xTaskOption);
                // got it out of reachability - now dispose/notify without lock

                aGuard.clear();
                m_rTreeManager.ConfigChangeBroadcaster::disposeBroadcastHelper(pInfo->pBroadcastHelper);

                OSL_ENSURE(pInfo->m_aNotificationList.empty(),
                            "WARNING: Empty TreeInfo still has notifications registered - will be leaked");

                pInfo = NULL;

                */
            }
            else
                CFG_TRACE_INFO_NI("- Currently no more cleanup tasks for this options set" );

            // clear the guard and throw away the nodes
            aGuard.clear();

            if (!aDisposeList.empty())
            {
                CFG_TRACE_INFO_NI("- Closing %d modules", int(aDisposeList.size()) );
                m_rTreeManager.closeModules(aDisposeList,rTaskOptions);
            }
            else
                CFG_TRACE_INFO_NI("- No modules trees to dispose");
        }
        else
            CFG_TRACE_INFO_NI("- No matching data container (TreeInfo) found - task is obsolete");
    }
    else
        CFG_TRACE_INFO("No eligible task found - may reschedule");

    return aNextTime;
}
// -------------------------------------------------------------------------

static inline
TimeStamp getExpirationTime( vos::OTimer const& aTimer )
{
    OSL_ENSURE(aTimer.isTicking(), "Timer is already expired !");

    // note: order ensures that result time may be earlier, but not later
    TimeStamp       const now ( TimeStamp::getCurrentTime() );
    TimeInterval    const left( aTimer.getRemainingTime() );
    TimeStamp       const expires = now + left;

    return expires;
}

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

#ifdef _DEBUG
static
void checkTimerStarted( vos::OTimer const& aTimer, TimeStamp const& _aLimit)
{
    const TimeInterval tolerance( vos::TTimeValue(1) ); // milliseconds
    if (aTimer.isTicking())
    {
        TimeStamp   const    expires = getExpirationTime(aTimer);
        TimeStamp   const    limit   = _aLimit + tolerance;

        OSL_ENSURE(expires <= limit, "Timer does not expire within expected time (tolerance 1 ms) !");
      //  OSL_ENSURE(expires <= _aLimit, "Timer does not expire within expected time !");
        OSL_ENSURE(aTimer.isTicking(), "Timer just started already expired ?!");
    }
    else
    {
        OSL_ENSURE(false, "Timer just started is not ticking ?!");
    }
}
#endif

// -------------------------------------------------------------------------
// should be called guarded only
void OTreeDisposeScheduler::implStartBefore(TimeStamp const& _aTime)
{
    // check if we were cleared
    if (!m_aAgenda.empty() && !_aTime.isNever())
    {
        if (!m_xTimer->isTicking() || _aTime < getExpirationTime(*m_xTimer))
        {
            m_xTimer->setAbsoluteTime(_aTime.getTimeValue());

            if (!m_xTimer->isTicking()) m_xTimer->start();

            OSL_DEBUG_ONLY( checkTimerStarted(*m_xTimer,_aTime) );
        }
        CFG_TRACE_INFO_NI("- Cleanup timer running - next execution in %d seconds", int (m_xTimer->getRemainingTime().Seconds) );
        CFG_TRACE_INFO_NI("- %d cleanup tasks are pending", int(m_aAgenda.size()) );
    }
    else
    {
        m_xTimer->stop();
        CFG_TRACE_INFO_NI("- Stopped timer - no more open cleanup tasks");
    }
}
// -------------------------------------------------------------------------

// should be called guarded only (m_aMutex must be locked)
TimeStamp OTreeDisposeScheduler::implAddTask(RequestOptions const& _aOptions, TimeStamp const& _aTime)
{
    typedef Agenda::value_type Task;

    // try to insert after euivalent entries (but STL may ignore the hint)
    Agenda::iterator where = m_aAgenda.upper_bound(_aTime);

    m_aAgenda.insert(where, Task(_aTime,_aOptions));

    OSL_ASSERT(!m_aAgenda.empty());

    return m_aAgenda.begin()->first;
}
// -------------------------------------------------------------------------

} // namespace
<commit_msg>INTEGRATION: CWS dbgmacros1 (1.17.40); FILE MERGED 2003/04/09 10:21:07 kso 1.17.40.1: #108413# - debug macro unification.<commit_after>/*************************************************************************
 *
 *  $RCSfile: disposetimer.cxx,v $
 *
 *  $Revision: 1.18 $
 *
 *  last change: $Author: vg $ $Date: 2003-04-15 17:18:49 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 by Sun Microsystems, Inc.
 *  901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License version 2.1, as published by the Free Software Foundation.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *  MA  02111-1307  USA
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (the "License"); You may not use this file
 *  except in compliance with the License. You may obtain a copy of the
 *  License at http://www.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 *
 *  Copyright: 2000 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): _______________________________________
 *
 *
 ************************************************************************/

#include <stdio.h>

#include "disposetimer.hxx"

#ifndef CONFIGMGR_BACKEND_CACHECONTROLLER_HXX
#include "cachecontroller.hxx"
#endif

#ifndef CONFIGMGR_CONFIGEXCEPT_HXX_
#include "configexcept.hxx"
#endif

#ifndef _CONFIGMGR_TRACER_HXX_
#include "tracer.hxx"
#endif

#ifndef _OSL_DIAGNOSE_H_
#include <osl/diagnose.h>
#endif

namespace configmgr
{
//==========================================================================
//=
//==========================================================================

void OTreeDisposeScheduler::scheduleCleanup(RequestOptions const& _aOptions)
{
    OSL_ENSURE(_aOptions.hasLocale(), "ERROR: OTreeDisposeScheduler: cannot handle complete user scheduling");

    osl::MutexGuard aGuard( m_aMutex );

    CFG_TRACE_INFO("Scheduling data cleanup for user '%s' with locale '%s'",
                    OUSTRING2ASCII(_aOptions.getEntity()),
                    OUSTRING2ASCII(_aOptions.getLocale()));

    CFG_TRACE_INFO_NI("- Cleanup will be started in about %d seconds", int(m_aCleanupDelay.getTimeValue().Seconds));

    TimeStamp aNewTime = implGetCleanupTime(TimeStamp::getCurrentTime(), m_aCleanupDelay);
    OSL_ASSERT(!aNewTime.isNever());

    TimeStamp aScheduleTime = implAddTask(_aOptions, aNewTime);

    OSL_ASSERT(aScheduleTime <= aNewTime);
    OSL_ASSERT(!aScheduleTime.isNever());

    implStartBefore(aNewTime);
}
// -------------------------------------------------------------------------

static
inline
bool equivalentOptions(RequestOptions const& lhs, RequestOptions const& rhs)
{
    lessRequestOptions lessThan;
    return ! (lessThan(lhs,rhs) || lessThan(rhs,lhs));
}
// -------------------------------------------------------------------------

void OTreeDisposeScheduler::clearTasks(RequestOptions const& _aOptions)
{
    osl::MutexGuard aOwnGuard( m_aMutex );

    CFG_TRACE_INFO("Cancelling all data cleanup tasks for user '%s' with locale '%s'",
                    OUSTRING2ASCII(_aOptions.getEntity()),
                    OUSTRING2ASCII(_aOptions.getLocale()));

    Agenda::iterator it = m_aAgenda.begin();
    while(it != m_aAgenda.end())
    {
        Agenda::iterator cur = it++;
        if (equivalentOptions(_aOptions,cur->second))
        {
            m_aAgenda.erase(cur);
            CFG_TRACE_INFO_NI("- One pending task canceled");
        }
    }
}
// -------------------------------------------------------------------------

void OTreeDisposeScheduler::stopAndClearTasks()
{
    osl::MutexGuard aOwnGuard( m_aMutex );

    CFG_TRACE_INFO("Cancelling all data cleanup tasks, Stopping Cleanup timer");
    CFG_TRACE_INFO_NI("- %d cleanup tasks were pending", int(m_aAgenda.size()) );

    if (m_xTimer.isValid())
        m_xTimer->dispose(); // just to be sure

    m_aAgenda.clear();
}
// -------------------------------------------------------------------------

OTreeDisposeScheduler::Task OTreeDisposeScheduler::getTask(TimeStamp const& _aActualTime, TimeStamp& _rNextTime)
{
    OSL_ASSERT( _rNextTime.isNever() ); // internal contract, we set this only in the positive case

    osl::MutexGuard aOwnGuard( m_aMutex );
    Task aTask( false, RequestOptions() );

    if (!m_aAgenda.empty())
    {
        Agenda::iterator const it = m_aAgenda.begin();

        if (it->first <= _aActualTime)
        {
            aTask = std::make_pair(true,it->second);
            m_aAgenda.erase(it);
        }
    }

    if (!m_aAgenda.empty())
    {
        Agenda::iterator const it = m_aAgenda.begin();

        _rNextTime = it->first;
    }

    return aTask;
}
// -------------------------------------------------------------------------

void OTreeDisposeScheduler::Timer::onShot()
{
    osl::MutexGuard aGuard(m_aMutex);
    if (pParent)
        pParent->onTimerShot();
}
// -------------------------------------------------------------------------

void OTreeDisposeScheduler::onTimerShot()
{
    CFG_TRACE_INFO("Cleanup Timer invoked - executing dispose task");

    TimeStamp aActualTime = TimeStamp::getCurrentTime();
    TimeStamp aNextTime = implGetCleanupTime(aActualTime, getCleanupInterval());

    try
    {
        TimeStamp aNextDisposeTime = runDisposer(aActualTime);

        if (aNextTime < aNextDisposeTime)
            aNextTime = aNextDisposeTime;
    }

    catch (uno::Exception& ue)
    {
        OSL_ENSURE(false, "ERROR: UNO Exception left a disposer");
        ue;
    }
    catch (configuration::Exception& ce)
    {
        OSL_ENSURE(false, "ERROR: configuration::Exception left a disposer");
        ce;
    }
    catch (...)
    {
        OSL_ENSURE(false, "ERROR: Unknown Exception left a disposer");
    }

    osl::MutexGuard aGuard(m_aMutex);
    implStartBefore(aNextTime);
}
// -------------------------------------------------------------------------

// this really should be a member of the TreeManager (see TreeManager::disposeOne etc.)
TimeStamp OTreeDisposeScheduler::runDisposer(TimeStamp const& _aActualTime)
{
    TimeStamp aNextTime = TimeStamp::never();
    OSL_ASSERT(aNextTime.isNever());

    osl::ClearableMutexGuard aGuard( m_rTreeManager.m_aCacheList.mutex() );

    Task aTask = this->getTask( _aActualTime, aNextTime );
    if (aTask.first)
    {
        RequestOptions & rTaskOptions = aTask.second;

        CFG_TRACE_INFO("Found cleanup task for user %s and locale %s",
                        OUSTRING2ASCII(rTaskOptions.getEntity()),
                        OUSTRING2ASCII(rTaskOptions.getLocale()));

        CacheManager::CacheRef aCache = m_rTreeManager.m_aCacheList.get(rTaskOptions);
        if (aCache.is())
        {
            CFG_TRACE_INFO_NI("- Found matching data container (TreeInfo) - collecting data");

            CacheLoadingAccess::DisposeList aDisposeList;

            TimeStamp aNextTaskTime = aCache->collectDisposeList(aDisposeList, _aActualTime, m_aCleanupDelay);

            CFG_TRACE_INFO_NI("- Found %d module trees to dispose", int(aDisposeList.size()) );

            if (!aNextTaskTime.isNever())
            {
                OSL_ENSURE( !aCache->isEmpty(), "ERROR: Empty TreeInfo returning finite dispose time");

                // repost with new time
                osl::MutexGuard aOwnGuard( m_aMutex );

                CFG_TRACE_INFO_NI("- Rescheduling current option set" );

                aNextTime = this->implAddTask(rTaskOptions,aNextTaskTime);
            }

            else if (aCache->isEmpty())// may have been the last one - check that
            {
                // currently it is not possible to release options which are
                // because it is not save to delete the info if another thread is running in
                // a read request

                /*

                CFG_TRACE_INFO_NI("- Disposing last data for this options set => Removing TreeInfo" );

                // get rid of it - see TreeManager::disposeOne
                std::auto_ptr<TreeInfo> pDisposeInfo(pInfo);

                m_rTreeManager.m_aTreeList.erase(xTaskOption);
                // got it out of reachability - now dispose/notify without lock

                aGuard.clear();
                m_rTreeManager.ConfigChangeBroadcaster::disposeBroadcastHelper(pInfo->pBroadcastHelper);

                OSL_ENSURE(pInfo->m_aNotificationList.empty(),
                            "WARNING: Empty TreeInfo still has notifications registered - will be leaked");

                pInfo = NULL;

                */
            }
            else
                CFG_TRACE_INFO_NI("- Currently no more cleanup tasks for this options set" );

            // clear the guard and throw away the nodes
            aGuard.clear();

            if (!aDisposeList.empty())
            {
                CFG_TRACE_INFO_NI("- Closing %d modules", int(aDisposeList.size()) );
                m_rTreeManager.closeModules(aDisposeList,rTaskOptions);
            }
            else
                CFG_TRACE_INFO_NI("- No modules trees to dispose");
        }
        else
            CFG_TRACE_INFO_NI("- No matching data container (TreeInfo) found - task is obsolete");
    }
    else
        CFG_TRACE_INFO("No eligible task found - may reschedule");

    return aNextTime;
}
// -------------------------------------------------------------------------

static inline
TimeStamp getExpirationTime( vos::OTimer const& aTimer )
{
    OSL_ENSURE(aTimer.isTicking(), "Timer is already expired !");

    // note: order ensures that result time may be earlier, but not later
    TimeStamp       const now ( TimeStamp::getCurrentTime() );
    TimeInterval    const left( aTimer.getRemainingTime() );
    TimeStamp       const expires = now + left;

    return expires;
}

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

#if OSL_DEBUG_LEVEL > 0
static
void checkTimerStarted( vos::OTimer const& aTimer, TimeStamp const& _aLimit)
{
    const TimeInterval tolerance( vos::TTimeValue(1) ); // milliseconds
    if (aTimer.isTicking())
    {
        TimeStamp   const    expires = getExpirationTime(aTimer);
        TimeStamp   const    limit   = _aLimit + tolerance;

        OSL_ENSURE(expires <= limit, "Timer does not expire within expected time (tolerance 1 ms) !");
      //  OSL_ENSURE(expires <= _aLimit, "Timer does not expire within expected time !");
        OSL_ENSURE(aTimer.isTicking(), "Timer just started already expired ?!");
    }
    else
    {
        OSL_ENSURE(false, "Timer just started is not ticking ?!");
    }
}
#endif

// -------------------------------------------------------------------------
// should be called guarded only
void OTreeDisposeScheduler::implStartBefore(TimeStamp const& _aTime)
{
    // check if we were cleared
    if (!m_aAgenda.empty() && !_aTime.isNever())
    {
        if (!m_xTimer->isTicking() || _aTime < getExpirationTime(*m_xTimer))
        {
            m_xTimer->setAbsoluteTime(_aTime.getTimeValue());

            if (!m_xTimer->isTicking()) m_xTimer->start();

            OSL_DEBUG_ONLY( checkTimerStarted(*m_xTimer,_aTime) );
        }
        CFG_TRACE_INFO_NI("- Cleanup timer running - next execution in %d seconds", int (m_xTimer->getRemainingTime().Seconds) );
        CFG_TRACE_INFO_NI("- %d cleanup tasks are pending", int(m_aAgenda.size()) );
    }
    else
    {
        m_xTimer->stop();
        CFG_TRACE_INFO_NI("- Stopped timer - no more open cleanup tasks");
    }
}
// -------------------------------------------------------------------------

// should be called guarded only (m_aMutex must be locked)
TimeStamp OTreeDisposeScheduler::implAddTask(RequestOptions const& _aOptions, TimeStamp const& _aTime)
{
    typedef Agenda::value_type Task;

    // try to insert after euivalent entries (but STL may ignore the hint)
    Agenda::iterator where = m_aAgenda.upper_bound(_aTime);

    m_aAgenda.insert(where, Task(_aTime,_aOptions));

    OSL_ASSERT(!m_aAgenda.empty());

    return m_aAgenda.begin()->first;
}
// -------------------------------------------------------------------------

} // namespace
<|endoftext|>


<commit_before>#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <functional>
#define __STDC_LIMIT_MACROS
#include <stdint.h>

#define LOG_AT_W  /* warning only */

extern "C" {
#include "stinger_core/xmalloc.h"
#include "stinger_core/stinger_error.h"
}

#include "rapidjson/document.h"
#include "rapidjson/rapidjson.h"
#include "rapidjson/stringbuffer.h"
#include "rapidjson/prettywriter.h"

#include "json_rpc_server.h"
#include "json_rpc.h"
#include "rpc_state.h"
#include "mon_handling.h"
#include "session_handling.h"

#include "mongoose/mongoose.h"

using namespace gt::stinger;

#define MAX_REQUEST_SIZE (1ULL << 22ULL)

static int
begin_request_handler(struct mg_connection *conn)
{
  const struct mg_request_info *request_info = mg_get_request_info(conn);
  LOG_D_A("Receiving request for %s", request_info->uri);

  if (strncmp(request_info->uri, "/data/", 6)==0) {
    return 0;
  }

  if (strncmp(request_info->uri, "/jsonrpc", 8)==0) {
    uint8_t * storage = (uint8_t *) xcalloc (1, MAX_REQUEST_SIZE);

    int64_t read = mg_read(conn, storage, MAX_REQUEST_SIZE);
    if (read > MAX_REQUEST_SIZE-2) {
      LOG_E_A("Request was too large: %ld", read);
      return -1;
    }
    storage[read] = '\0';

    LOG_D_A("Parsing request:\n%.*s", read, storage);
    rapidjson::Document input, output;
    input.Parse<0>((char *)storage);

    json_rpc_process_request(input, output);

    rapidjson::StringBuffer out_buf;
    rapidjson::PrettyWriter<rapidjson::StringBuffer> writer(out_buf);
    output.Accept(writer);

    const char * out_ch = out_buf.GetString();
    int out_len = out_buf.Size();

    LOG_D_A("RapidJSON parsed :%d\n%s", out_len, out_ch);

    int code = mg_printf(conn,
	      "HTTP/1.1 200 OK\r\n"
	      "Content-Type: text/plain\r\n"
	      "Content-Length: %d\r\n"        // Always set Content-Length
	      "Access-Control-Allow-Origin: *\r\n"
	      "\r\n"
	      "%s",
	      out_len, out_ch);

    LOG_D_A("Code was %d", code);

    free(storage);

    return 1;
  }

  return 0;
}

int
main (int argc, char ** argv)
{
  int unleash_daemon = 0;

  int opt = 0;
  while(-1 != (opt = getopt(argc, argv, "h?d"))) {
    switch(opt) {
      default: {
	LOG_E_A("Unknwon option %c", opt);
      } /* no break */
      case '?':
      case 'h': {
	printf("Usage: %s [-d]\n", argv[0]);
	printf("-d\tdaemon mode\n");
	exit(-1);
      } break;

      case 'd': {
	unleash_daemon = 1;
      } break;
    }
  }

  JSON_RPCServerState & server_state = JSON_RPCServerState::get_server_state();
  server_state.add_rpc_function("get_algorithms", new JSON_RPC_get_algorithms(&server_state));
  server_state.add_rpc_function("get_data_description", new JSON_RPC_get_data_description(&server_state));
  server_state.add_rpc_function("get_data_array_range", new JSON_RPC_get_data_array_range(&server_state));
  server_state.add_rpc_function("get_data_array_sorted_range",	new JSON_RPC_get_data_array_sorted_range(&server_state));
  server_state.add_rpc_function("get_data_array", new JSON_RPC_get_data_array(&server_state));
  server_state.add_rpc_function("get_data_array_set", new JSON_RPC_get_data_array_set(&server_state));
  server_state.add_rpc_function("get_graph_stats", new JSON_RPC_get_graph_stats(&server_state));
  server_state.add_rpc_function("label_breadth_first_search", new JSON_RPC_label_breadth_first_search(&server_state));
  server_state.add_rpc_function("breadth_first_search", new JSON_RPC_breadth_first_search(&server_state));
  server_state.add_rpc_function("register", new JSON_RPC_register(&server_state));
  server_state.add_rpc_function("request", new JSON_RPC_request(&server_state));
  server_state.add_rpc_function("get_data_array_reduction", new JSON_RPC_get_data_array_reduction(&server_state));

  server_state.add_rpc_session("subgraph", new JSON_RPC_community_subgraph(0, &server_state));
  server_state.add_rpc_session("vertex_event_notifier", new JSON_RPC_vertex_event_notifier(0, &server_state));
  server_state.add_rpc_session("get_latlon", new JSON_RPC_get_latlon(0, &server_state));
  server_state.add_rpc_session("get_latlon_gnip", new JSON_RPC_get_latlon_gnip(0, &server_state));

  mon_connect(10103, "localhost", "json_rpc");

  /* Mongoose setup and start */
  struct mg_context *ctx;
  struct mg_callbacks callbacks;

  // List of options. Last element must be NULL.
  const char *opts[] = {"listening_ports", "8088", "document_root", "data", NULL};

  // Prepare callbacks structure. We have only one callback, the rest are NULL.
  memset(&callbacks, 0, sizeof(callbacks));
  callbacks.begin_request = begin_request_handler;

  // Start the web server.
  ctx = mg_start(&callbacks, NULL, opts);

  /* infinite loop */
  if(unleash_daemon) {
    while(1) { sleep(10); }
  } else {
    printf("Press <q> to shut down the server...\n");
    while (getchar() != 'q');
  }

  return 0;
}
<commit_msg>Changing JSON-RPC Server to use TCLAP for commandline arguments.<commit_after>#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <functional>
#define __STDC_LIMIT_MACROS
#include <stdint.h>
#include <tclap/CmdLine.h>

#define LOG_AT_W  /* warning only */

extern "C" {
#include "stinger_core/xmalloc.h"
#include "stinger_core/stinger_error.h"
}

#include "rapidjson/document.h"
#include "rapidjson/rapidjson.h"
#include "rapidjson/stringbuffer.h"
#include "rapidjson/prettywriter.h"

#include "json_rpc_server.h"
#include "json_rpc.h"
#include "rpc_state.h"
#include "mon_handling.h"
#include "session_handling.h"

#include "mongoose/mongoose.h"

using namespace gt::stinger;

#define MAX_REQUEST_SIZE (1ULL << 22ULL)

static int
begin_request_handler(struct mg_connection *conn)
{
  const struct mg_request_info *request_info = mg_get_request_info(conn);
  LOG_D_A("Receiving request for %s", request_info->uri);

  if (strncmp(request_info->uri, "/data/", 6)==0) {
    return 0;
  }

  if (strncmp(request_info->uri, "/jsonrpc", 8)==0) {
    uint8_t * storage = (uint8_t *) xcalloc (1, MAX_REQUEST_SIZE);

    int64_t read = mg_read(conn, storage, MAX_REQUEST_SIZE);
    if (read > MAX_REQUEST_SIZE-2) {
      LOG_E_A("Request was too large: %ld", read);
      return -1;
    }
    storage[read] = '\0';

    LOG_D_A("Parsing request:\n%.*s", read, storage);
    rapidjson::Document input, output;
    input.Parse<0>((char *)storage);

    json_rpc_process_request(input, output);

    rapidjson::StringBuffer out_buf;
    rapidjson::PrettyWriter<rapidjson::StringBuffer> writer(out_buf);
    output.Accept(writer);

    const char * out_ch = out_buf.GetString();
    int out_len = out_buf.Size();

    LOG_D_A("RapidJSON parsed :%d\n%s", out_len, out_ch);

    int code = mg_printf(conn,
	      "HTTP/1.1 200 OK\r\n"
	      "Content-Type: text/plain\r\n"
	      "Content-Length: %d\r\n"        // Always set Content-Length
	      "Access-Control-Allow-Origin: *\r\n"
	      "\r\n"
	      "%s",
	      out_len, out_ch);

    LOG_D_A("Code was %d", code);

    free(storage);

    return 1;
  }

  return 0;
}

int
main (int argc, char ** argv)
{
  bool unleash_daemon = false;

  try {
    /* parse command line configuration */
    TCLAP::CmdLine cmd("JSON-RPC Server", ' ', "2.0");
    TCLAP::SwitchArg daemonSwitch ("d", "daemon", "Daemon mode", cmd, false);
    cmd.parse (argc, argv);

    unleash_daemon = daemonSwitch.getValue();
  } catch (TCLAP::ArgException &e)
  { std::cerr << "error: " << e.error() << " for arg " << e.argId() << std::endl; return 0; }

  JSON_RPCServerState & server_state = JSON_RPCServerState::get_server_state();
  server_state.add_rpc_function("get_algorithms", new JSON_RPC_get_algorithms(&server_state));
  server_state.add_rpc_function("get_data_description", new JSON_RPC_get_data_description(&server_state));
  server_state.add_rpc_function("get_data_array_range", new JSON_RPC_get_data_array_range(&server_state));
  server_state.add_rpc_function("get_data_array_sorted_range",	new JSON_RPC_get_data_array_sorted_range(&server_state));
  server_state.add_rpc_function("get_data_array", new JSON_RPC_get_data_array(&server_state));
  server_state.add_rpc_function("get_data_array_set", new JSON_RPC_get_data_array_set(&server_state));
  server_state.add_rpc_function("get_graph_stats", new JSON_RPC_get_graph_stats(&server_state));
  server_state.add_rpc_function("label_breadth_first_search", new JSON_RPC_label_breadth_first_search(&server_state));
  server_state.add_rpc_function("breadth_first_search", new JSON_RPC_breadth_first_search(&server_state));
  server_state.add_rpc_function("register", new JSON_RPC_register(&server_state));
  server_state.add_rpc_function("request", new JSON_RPC_request(&server_state));
  server_state.add_rpc_function("get_data_array_reduction", new JSON_RPC_get_data_array_reduction(&server_state));

  server_state.add_rpc_session("subgraph", new JSON_RPC_community_subgraph(0, &server_state));
  server_state.add_rpc_session("vertex_event_notifier", new JSON_RPC_vertex_event_notifier(0, &server_state));
  server_state.add_rpc_session("get_latlon", new JSON_RPC_get_latlon(0, &server_state));
  server_state.add_rpc_session("get_latlon_gnip", new JSON_RPC_get_latlon_gnip(0, &server_state));

  mon_connect(10103, "localhost", "json_rpc");

  /* Mongoose setup and start */
  struct mg_context *ctx;
  struct mg_callbacks callbacks;

  // List of options. Last element must be NULL.
  const char *opts[] = {"listening_ports", "8088", "document_root", "data", NULL};

  // Prepare callbacks structure. We have only one callback, the rest are NULL.
  memset(&callbacks, 0, sizeof(callbacks));
  callbacks.begin_request = begin_request_handler;

  // Start the web server.
  ctx = mg_start(&callbacks, NULL, opts);

  /* infinite loop */
  if(unleash_daemon) {
    while(1) { sleep(10); }
  } else {
    printf("Press <q> to shut down the server...\n");
    while (getchar() != 'q');
  }

  return 0;
}
<|endoftext|>


<commit_before>#ifndef INCLUDED_SROOK_IOS_BIFSTREAM_HPP
#define INCLUDED_SROOK_IOS_BIFSTREAM_HPP
#include<srook/config/noexcept_detection.hpp>
#include<srook/cstddef/byte.hpp>
#include<srook/config/require.hpp>
#include<srook/type_traits/is_callable.hpp>
#include<srook/type_traits/has_iterator.hpp>
#include<srook/optional.hpp>
#include<fstream>
#include<type_traits>
#include<array>
#include<memory>
#include<algorithm>

namespace srook{
inline namespace v1{

struct bifstream final : std::ifstream {
	using std::ifstream::ifstream;

	explicit bifstream(const char* filename,const std::ios_base::openmode& open_mode = std::ios::in | std::ios::binary)
		:std::ifstream{},
		buffer{},
		last(nullptr),
		forward_iter(nullptr)
	{
		try{
			exceptions(std::ios::eofbit | std::ios::failbit | std::ios::badbit);
			open(filename,open_mode);
			seekg(0,std::fstream::end);
			const std::size_t end_pos = std::size_t(tellg());
			clear();
			seekg(0,std::fstream::beg);
			const std::size_t begin_pos = std::size_t(tellg());
			const std::size_t size = end_pos - begin_pos;

			buffer = std::make_unique<srook::byte[]>(size);

			exceptions(std::ios::badbit);
			read(reinterpret_cast<char*>(buffer.get()),long(size));
			close();

			forward_iter = buffer.get();
			last = std::next(buffer.get(),size);
		}catch(const std::ios_base::failure& e){
			buffer.reset();
			nextflag = readflag = false;
			last = forward_iter = nullptr;
			bit_position = srook::nullopt;
		}
	}

	void skip_byte(std::size_t n)
	{
		if(forward_iter + n > last)throw std::out_of_range(__func__);
		if((forward_iter += n) >= last)readflag = false;
		bit_position = 7;
	}

	explicit operator bool()const noexcept
	{
		return readflag;
	}

	std::size_t size()const noexcept
	{
		return last - buffer.get();
	}

	srook::byte* next_address()const noexcept
	{
		if(!bit_position)return nullptr;
		else if(bit_position.value() == 7)return forward_iter;
		else if(forward_iter < last)return forward_iter + 1;
		else return nullptr;
	}
private:
	struct tag_argument{
		explicit constexpr tag_argument(int num):n(std::move(num)){}
		int n;
	};
public:
	static constexpr auto Byte = []{};
	static constexpr auto Word = []{};
	static constexpr auto Bytes = []{};

	struct Byte_n : tag_argument{
		using tag_argument::tag_argument;
	};
	struct Bits : tag_argument{
		using tag_argument::tag_argument;
	};
private:
	std::unique_ptr<srook::byte[]> buffer;
	srook::byte* last;
	srook::byte* forward_iter;

	srook::optional<int> bit_position = 7u;
	const std::array<int,8> bit_fullmask {{ 0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80 }};
	bool nextflag = true;
	bool readflag = true;
private:
	void increment_buffer()noexcept
	{
		if(++forward_iter >= last)readflag = false;
	}

	template<class T,std::size_t v>
	friend std::pair<const decltype(Bytes)&,bifstream&>
	operator>>(std::pair<const decltype(Bytes)&,bifstream&> p,const std::array<T,v>& ar)noexcept(false)
	{
		bifstream& this_ = p.second;

		if(!this_.bit_position)throw std::runtime_error("invalid construct");

		if(this_.bit_position.value() != 7){
			this_.increment_buffer();
			this_.bit_position = 7;
		}
		if(this_.forward_iter + ar.size() > this_.last)throw std::out_of_range(__func__);
		std::copy(this_.forward_iter,this_.forward_iter + ar.size(),std::begin(ar));
		if((this_.forward_iter += ar.size()) >= this_.last)this_.readflag = false;

		return p;
	}

	template<class Value,REQUIRES(!srook::is_callable_v<std::decay_t<Value>> and !srook::has_iterator_v<std::decay_t<Value>>)>
	friend std::pair<const decltype(Byte)&,bifstream&>
   	operator>>(std::pair<const decltype(Byte)&,bifstream&> p,Value& src)noexcept(false)
	{
		bifstream& this_ = p.second;

		if(!this_.bit_position)throw std::runtime_error("invalid construct");

		if(this_.readflag){
			if(this_.bit_position.value() != 7){
				this_.increment_buffer();
				this_.bit_position = 7;
			}
			src = Value(*this_.forward_iter);
			this_.increment_buffer();
			this_.nextflag = true;
		}else{
			throw std::runtime_error(__func__);
		}

		return p;
	}

	template<class T,REQUIRES(std::is_integral<std::decay_t<T>>::value)>
	friend std::pair<const decltype(Word)&,bifstream&>
	operator>>(std::pair<const decltype(Word)&,bifstream&> p,T& value)noexcept(false)
	{
		bifstream& this_ = p.second;

		if(!this_.bit_position)throw std::runtime_error("invalid construct");

		if(this_.readflag){
			if(this_.bit_position.value() != 7){
				this_.increment_buffer();
				this_.bit_position = 7;
			}
			value = srook::to_integer<T>(*this_.forward_iter) << 8;
			this_.increment_buffer();
			value |= srook::to_integer<T>(*this_.forward_iter);
			this_.increment_buffer();
		}else{
			throw std::runtime_error(__func__);
		}
		return p;
	}

	template<class T,REQUIRES(std::is_integral<std::decay_t<std::decay_t<T>>>::value)>
	friend std::pair<Bits,bifstream&>
	operator>>(std::pair<Bits,bifstream&> p,T& value)noexcept(false)
	{
		if(p.first.n <= 0)throw std::invalid_argument(__func__);

		bifstream& this_ = p.second;
		if(!this_.bit_position) throw std::runtime_error("invalid construct");

		int r = 0;
		for(srook::byte c{}; p.first.n; --p.first.n){
			if(this_.bit_position.value() < 0){
				this_.bit_position = 7;
				this_.increment_buffer();
				if(!this_.nextflag){
					this_.increment_buffer();
					this_.nextflag = true;
				}

				c = *this_.forward_iter;
				if(srook::to_integer<std::underlying_type_t<srook::byte>>(c) == 0xff){
					c = *(this_.forward_iter + 1);
					if(srook::to_integer<bool>(c)){
						throw std::runtime_error(__func__);
					}
					this_.nextflag = false;
				}
			}
			r <<= 1;
			r |= (srook::to_integer<int>(*this_.forward_iter) & this_.bit_fullmask[std::size_t(this_.bit_position.value())]) ? 1 : 0;
			this_.bit_position = this_.bit_position.value() - 1;
		}
		value = T(r);

		return p;
	}

	template<class Range,REQUIRES(srook::has_iterator_v<std::decay_t<Range>>)>
	friend std::pair<const Byte_n&,bifstream&>
	operator>>(std::pair<const Byte_n&,bifstream&> p,Range& r)noexcept(false)
	{
		bifstream& this_ = p.second;

		if(this_.bit_position != 7){
			this_.increment_buffer();
			this_.bit_position = 7;
		}
		if(this_.forward_iter + p.first.n > this_.last)throw std::out_of_range(__func__);

		if(r.size() >= std::size_t(p.first.n)){
			std::generate(std::begin(r),std::end(r),[&this_]{return typename Range::value_type(*this_.forward_iter++);});
		}else{
			r.resize(std::size_t(p.first.n));
			std::generate(std::begin(r),std::end(r),[&this_]{return typename Range::value_type(*this_.forward_iter++);});
		}
		if(this_.forward_iter >= this_.last)this_.readflag = false;

		return p;
	}

	template<class Tag>
	constexpr friend std::pair<Tag,bifstream&> operator|(bifstream& ofps,const Tag& t)
	SROOK_NOEXCEPT(std::make_pair(t,std::ref(ofps)))
	{
		static_assert(
			std::is_same_v<Tag,Byte_n> or
			std::is_same_v<Tag,std::decay_t<decltype(Byte)>> or
			std::is_same_v<Tag,std::decay_t<decltype(Bytes)>> or
			std::is_same_v<Tag,std::decay_t<decltype(Word)>> or
			"Invalid tag"
		);
		return std::make_pair(t,std::ref(ofps));
	}

	friend std::pair<Bits,bifstream&> operator|(bifstream& ofps,const Bits& bits)
	SROOK_NOEXCEPT(std::make_pair(bits,std::ref(ofps)))
	{
		return std::make_pair(bits,std::ref(ofps));
	}
};

} // inline namespace v1
} // namespace srook
#endif
<commit_msg>fix bifstream<commit_after>#ifndef INCLUDED_SROOK_IOS_BIFSTREAM_HPP
#define INCLUDED_SROOK_IOS_BIFSTREAM_HPP
#include<srook/config/noexcept_detection.hpp>
#include<srook/cstddef/byte.hpp>
#include<srook/config/require.hpp>
#include<srook/type_traits/is_callable.hpp>
#include<srook/type_traits/has_iterator.hpp>
#include<srook/optional.hpp>
#include<fstream>
#include<type_traits>
#include<array>
#include<memory>
#include<algorithm>

namespace srook{
inline namespace v1{

struct bifstream final : std::ifstream {
	using std::ifstream::ifstream;

	explicit bifstream(const char* filename,const std::ios_base::openmode& open_mode = std::ios::in | std::ios::binary)
		:std::ifstream{},
		buffer{},
		last(nullptr),
		forward_iter(nullptr)
	{
		try{
			exceptions(std::ios::eofbit | std::ios::failbit | std::ios::badbit);
			open(filename,open_mode);
			seekg(0,std::fstream::end);
			const std::size_t end_pos = std::size_t(tellg());
			clear();
			seekg(0,std::fstream::beg);
			const std::size_t begin_pos = std::size_t(tellg());
			const std::size_t size = end_pos - begin_pos;

			buffer = std::make_unique<srook::byte[]>(size);

			exceptions(std::ios::badbit);
			read(reinterpret_cast<char*>(buffer.get()),long(size));
			close();

			forward_iter = buffer.get();
			last = std::next(buffer.get(),size);
		}catch(const std::ios_base::failure& e){
			buffer.reset();
			nextflag = readflag = false;
			last = forward_iter = nullptr;
			bit_position = srook::nullopt;
		}
	}

	void skip_byte(std::size_t n)
	{
		if(forward_iter + n > last)throw std::out_of_range(__func__);
		if((forward_iter += n) >= last)readflag = false;
		bit_position = 7;
	}

	explicit operator bool()const noexcept
	{
		return readflag;
	}

	std::size_t size()const noexcept
	{
		return last - buffer.get();
	}

	srook::byte* next_address()const noexcept
	{
		if(bit_position == 7){
			return forward_iter;
		}else if(forward_iter < last){
			return forward_iter + 1;
		}else{
			return nullptr;
		}
	}
private:
	struct tag_argument{
		explicit constexpr tag_argument(int num):n(std::move(num)){}
		int n;
	};
public:
	static constexpr auto Byte = []{};
	static constexpr auto Word = []{};
	static constexpr auto Bytes = []{};

	struct Byte_n : tag_argument{
		using tag_argument::tag_argument;
	};
	struct Bits : tag_argument{
		using tag_argument::tag_argument;
	};
private:
	std::unique_ptr<srook::byte[]> buffer;
	srook::byte* last;
	srook::byte* forward_iter;

	srook::optional<int> bit_position = 7;
	const std::array<int,8> bit_fullmask {{ 0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80 }};
	bool nextflag = true;
	bool readflag = true;
private:
	void increment_buffer()noexcept
	{
		if(++forward_iter >= last)readflag = false;
	}

	template<class Range,REQUIRES(srook::has_iterator_v<std::decay_t<Range>>)>
	friend std::pair<const decltype(Bytes)&,bifstream&>
	operator>>(std::pair<const decltype(Bytes)&,bifstream&> p,Range& ar)noexcept(false)
	{
		bifstream& this_ = p.second;

		if(!this_.bit_position)throw std::runtime_error("invalid construct");

		if(this_.bit_position.value() != 7){
			this_.increment_buffer();
			this_.bit_position = 7;
		}
		if(this_.forward_iter + ar.size() > this_.last)throw std::out_of_range("Bytes operator<<");

		std::generate(std::begin(ar),std::end(ar),[&this_](){return typename std::decay_t<Range>::value_type(*this_.forward_iter++);});
		if((this_.forward_iter) >= this_.last)this_.readflag = false;

		return p;
	}

	template<class Value,REQUIRES(!srook::is_callable_v<std::decay_t<Value>> and !srook::has_iterator_v<std::decay_t<Value>>)>
	friend std::pair<const decltype(Byte)&,bifstream&>
   	operator>>(std::pair<const decltype(Byte)&,bifstream&> p,Value& src)noexcept(false)
	{
		bifstream& this_ = p.second;

		if(!this_.bit_position)throw std::runtime_error("invalid construct");

		if(this_.readflag){
			if(this_.bit_position.value() != 7){
				this_.increment_buffer();
				this_.bit_position = 7;
			}
			src = Value(*this_.forward_iter);
			this_.increment_buffer();
			this_.nextflag = true;
		}else{
			throw std::runtime_error("Byte operator<<: cannot read");
		}

		return p;
	}

	template<class T,REQUIRES(std::is_integral<std::decay_t<T>>::value)>
	friend std::pair<const decltype(Word)&,bifstream&>
	operator>>(std::pair<const decltype(Word)&,bifstream&> p,T& value)noexcept(false)
	{
		bifstream& this_ = p.second;

		if(!this_.bit_position)throw std::runtime_error("invalid construct");

		if(this_.readflag){
			if(this_.bit_position.value() != 7){
				this_.increment_buffer();
				this_.bit_position = 7;
			}
			unsigned int r = srook::to_integer<unsigned>(*this_.forward_iter) << 8;
			this_.increment_buffer();
			r |= srook::to_integer<unsigned>(*this_.forward_iter);
			this_.increment_buffer();
			value = T(r);
		}else{
			throw std::runtime_error("Word operator<<: cannot read");
		}
		return p;
	}

	template<class T,REQUIRES(std::is_integral<std::decay_t<std::decay_t<T>>>::value)>
	friend std::pair<Bits,bifstream&>
	operator>>(std::pair<Bits,bifstream&> p,T& value)noexcept(false)
	{
		if(p.first.n <= 0){
			value = T(0);
			return p;
		}

		bifstream& this_ = p.second;
		if(!this_.bit_position) throw std::runtime_error("invalid construct");

		int r = 0;
		for(srook::byte c{}; p.first.n; --p.first.n){
			if(this_.bit_position.value() < 0){
				this_.bit_position = 7;
				this_.increment_buffer();
				if(!this_.nextflag){
					this_.increment_buffer();
					this_.nextflag = true;
				}

				c = *this_.forward_iter;
				if(srook::to_integer<std::underlying_type_t<srook::byte>>(c) == 0xff){
					c = *(this_.forward_iter + 1);
					if(srook::to_integer<std::underlying_type_t<srook::byte>>(c) != 0){
						value = -srook::to_integer<std::underlying_type_t<srook::byte>>(c);
					}
					this_.nextflag = false;
				}
			}
			r <<= 1;
			r |= (srook::to_integer<std::underlying_type_t<srook::byte>>(*this_.forward_iter) & this_.bit_fullmask[std::size_t(this_.bit_position.value())]) ? 1 : 0;
			this_.bit_position = this_.bit_position.value() - 1;
		}
		value = T(r);
		return p;
	}

	template<class Range,REQUIRES(srook::has_iterator_v<std::decay_t<Range>>)>
	friend std::pair<const Byte_n&,bifstream&>
	operator>>(std::pair<const Byte_n&,bifstream&> p,Range& r)noexcept(false)
	{
		bifstream& this_ = p.second;

		if(this_.bit_position != 7){
			this_.increment_buffer();
			this_.bit_position = 7;
		}
		if(this_.forward_iter + p.first.n > this_.last)throw std::out_of_range("Byte_n operator>> out of range");

		if(r.size() >= std::size_t(p.first.n)){
			std::generate(std::begin(r),std::end(r),[&this_]{return typename Range::value_type(*this_.forward_iter++);});
		}else{
			r.resize(std::size_t(p.first.n));
			std::generate(std::begin(r),std::end(r),[&this_]{return typename Range::value_type(*this_.forward_iter++);});
		}
		if(this_.forward_iter >= this_.last)this_.readflag = false;

		return p;
	}

	template<class Tag>
	constexpr friend std::pair<Tag,bifstream&> operator|(bifstream& ofps,const Tag& t)
	SROOK_NOEXCEPT(std::make_pair(t,std::ref(ofps)))
	{
		static_assert(
			std::is_same_v<Tag,Byte_n> or
			std::is_same_v<Tag,std::decay_t<decltype(Byte)>> or
			std::is_same_v<Tag,std::decay_t<decltype(Bytes)>> or
			std::is_same_v<Tag,std::decay_t<decltype(Word)>> or
			"Invalid tag"
		);
		return std::make_pair(t,std::ref(ofps));
	}

	friend std::pair<Bits,bifstream&> operator|(bifstream& ofps,const Bits& bits)
	SROOK_NOEXCEPT(std::make_pair(bits,std::ref(ofps)))
	{
		return std::make_pair(bits,std::ref(ofps));
	}
};

} // inline namespace v1
} // namespace srook
#endif
<|endoftext|>