<commit_before>// Copyright (c) 2015, LAAS-CNRS
// Authors: Joseph Mirabel (joseph.mirabel@laas.fr)
//
// This file is part of hpp-core.
// hpp-core is free software: you can redistribute it
// and/or modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation, either version
// 3 of the License, or (at your option) any later version.
//
// hpp-core 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
// General Lesser Public License for more details.  You should have
// received a copy of the GNU Lesser General Public License along with
// hpp-core. If not, see <http://www.gnu.org/licenses/>.

#include "hpp/core/locked-joint.hh"

#include <sstream>

#include <boost/assign/list_of.hpp>

#include <hpp/util/debug.hh>
#include <hpp/pinocchio/configuration.hh>
#include <hpp/pinocchio/device.hh>
#include <hpp/pinocchio/joint.hh>

#include <hpp/constraints/affine-function.hh>

namespace hpp {
  namespace core {
    using boost::assign::list_of;
    using constraints::ConstantFunction;
    namespace {
        template <typename T>
	std::string numToStr (const T& v) {
 	  std::stringstream ss; ss<<v; return ss.str ();
	}

        DifferentiableFunctionPtr_t makeFunction (
            const LiegroupElement& lge, const std::string& name)
        {
          return DifferentiableFunctionPtr_t
            (new ConstantFunction (lge, 0, 0, "LockedJoint " + name));
        }
    }

    /// Copy object and return shared pointer to copy
    EquationPtr_t LockedJoint::copy () const
    {
      return createCopy (weak_.lock ());
    }

    LockedJointPtr_t LockedJoint::create (const JointPtr_t& joint,
					  const LiegroupElement& value)
    {
      LockedJoint* ptr = new LockedJoint (joint, value);
      LockedJointPtr_t shPtr (ptr);
      ptr->init (shPtr);
      return shPtr;
    }

    LockedJointPtr_t LockedJoint::create (const JointPtr_t& joint,
                                          const size_type index,
					  vectorIn_t value)
    {
      LockedJoint* ptr = new LockedJoint (joint, index, value);
      LockedJointPtr_t shPtr (ptr);
      ptr->init (shPtr);
      return shPtr;
    }

    LockedJointPtr_t LockedJoint::create (const DevicePtr_t& dev,
        const size_type index, vectorIn_t value)
    {
      LockedJoint* ptr = new LockedJoint (dev, index, value);
      LockedJointPtr_t shPtr (ptr);
      ptr->init (shPtr);
      return shPtr;
    }

    LockedJointPtr_t LockedJoint::createCopy (LockedJointConstPtr_t other)
    {
      LockedJoint* ptr = new LockedJoint (*other);
      LockedJointPtr_t shPtr (ptr);
      ptr->init (shPtr);
      return shPtr;
    }

    size_type LockedJoint::rankInConfiguration () const
    {
      return outputConf_[0].first;
    }

    size_type LockedJoint::rankInVelocity () const
    {
      return outputVelocity_[0].first;
    }

    size_type LockedJoint::configSize () const
    {
      return configSpace_->nq ();
    }

    size_type LockedJoint::numberDof () const
    {
      return configSpace_->nv ();
    }

    const LiegroupSpacePtr_t& LockedJoint::configSpace () const
    {
      return configSpace_;
    }

    bool LockedJoint::isSatisfied (ConfigurationIn_t config)
    {
      vector_t error;
      return isSatisfied (config, error);
    }

    bool LockedJoint::isSatisfied (ConfigurationIn_t config, vector_t& error)
    {
      LiegroupElement q (config.segment (rankInConfiguration(), configSize ()),
                         configSpace_);
      error = q - (configSpace_->exp (rightHandSide ()));
      return error.isApprox (vector_t::Zero (joint_->numberDof ()));
    }

    void LockedJoint::rightHandSideFromConfig (ConfigurationIn_t config)
    {
      if (!constantRightHandSide ()) {
        LiegroupElement q (config.segment (rankInConfiguration(), configSize ()),
                           configSpace_);
        rightHandSide (q - configSpace_->neutral ());
      }
    }

    LockedJoint::LockedJoint (const JointPtr_t& joint,
                              const LiegroupElement& value) :
      ExplicitNumericalConstraint (
          joint->robot(),
          makeFunction (value.space()->neutral(), joint->name()),
          segments_t(), // input conf
          segments_t(), // input vel
          list_of(segment_t (joint->rankInConfiguration(), joint->configSize())), // output conf
          list_of(segment_t (joint->rankInVelocity     (), joint->numberDof ()))  // output vel
          ),
      jointName_ (joint->name ()),
      configSpace_ (joint->configurationSpace ())
    {
      assert (rhsSize () == joint->numberDof ());
      assert (*(value.space ()) == *configSpace_);
      rightHandSide (value - configSpace_->neutral ());
    }

    LockedJoint::LockedJoint (const JointPtr_t& joint, const size_type index,
        vectorIn_t value) :
      ExplicitNumericalConstraint (
          joint->robot(),
          makeFunction (
            LiegroupSpace::Rn (joint->configSize () - index)->neutral(),
            "partial_" + joint->name()),
          segments_t(), // input conf
          segments_t(), // input vel
          list_of(segment_t (joint->rankInConfiguration(), joint->configSize()-index)), // output conf
          list_of(segment_t (joint->rankInVelocity     (), joint->numberDof ()-index))  // output vel
          ),
      jointName_ ("partial_" + joint->name ()),
      joint_ (joint),
      configSpace_ (LiegroupSpace::Rn (joint->configSize () - index))
    {
      assert (joint->numberDof () == joint->configSize ());
      rightHandSide (value);
      assert (rhsSize () == value.size());
    }

    LockedJoint::LockedJoint (const DevicePtr_t& dev, const size_type index,
        vectorIn_t value) :
      ExplicitNumericalConstraint (
          dev,
          makeFunction (LiegroupSpace::Rn (value.size ())->neutral(),
            dev->name() + "_extraDof" + numToStr (index)),
          segments_t(), // input conf
          segments_t(), // input vel
          list_of(segment_t (
              dev->configSize () - dev->extraConfigSpace().dimension() + index,
              value.size())), // output conf
          list_of(segment_t (
              dev->numberDof ()  - dev->extraConfigSpace().dimension() + index,
              value.size()))  // output vel
          ),
      jointName_ (dev->name() + "_extraDof" + numToStr (index)),
      joint_ (JointPtr_t ()), configSpace_ (LiegroupSpace::Rn (value.size ()))
    {
      assert (value.size() > 0);
      assert (rankInConfiguration() + value.size() <= dev->configSize());
      rightHandSide (value);
      assert (rhsSize () == value.size());
    }

    void LockedJoint::init (const LockedJointPtr_t& self)
    {
      ExplicitNumericalConstraint::init(self);
      weak_ = self;
    }

    std::ostream& LockedJoint::print (std::ostream& os) const
    {
      os << "Locked joint " << jointName_
	 << ", value = " << pinocchio::displayConfig (rightHandSide ())
        << ": rank in configuration = " << rankInConfiguration()
        << ": rank in velocity = " << rankInVelocity()
        << std::endl;
      return os;
    }

    LockedJoint::LockedJoint (const LockedJoint& other) :
      ExplicitNumericalConstraint (other), jointName_ (other.jointName_),
      joint_ (other.joint_), configSpace_ (other.configSpace_), weak_ ()
    {
    }

    bool LockedJoint::isEqual (const Equation& other, bool swapAndTest) const
    {
      try {
	const LockedJoint& lj =
	  dynamic_cast <const LockedJoint&> (other);
	if (!Equation::isEqual (other, false)) return false;
	if (jointName_ != lj.jointName_) return false;
	if (rankInConfiguration() != lj.rankInConfiguration()) return false;
	if (rankInVelocity() != lj.rankInVelocity()) return false;
	if (*configSpace_ != *(lj.configSpace_)) return false;
	if (swapAndTest) return lj.isEqual (*this, false);
	return true;
      } catch (const std::bad_cast& err) {
	return false;
      }
    }
  } // namespace core
} // namespace hpp
<commit_msg>LockedJoint error is centered around the initial value<commit_after>// Copyright (c) 2015, LAAS-CNRS
// Authors: Joseph Mirabel (joseph.mirabel@laas.fr)
//
// This file is part of hpp-core.
// hpp-core is free software: you can redistribute it
// and/or modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation, either version
// 3 of the License, or (at your option) any later version.
//
// hpp-core 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
// General Lesser Public License for more details.  You should have
// received a copy of the GNU Lesser General Public License along with
// hpp-core. If not, see <http://www.gnu.org/licenses/>.

#include "hpp/core/locked-joint.hh"

#include <sstream>

#include <boost/assign/list_of.hpp>

#include <hpp/util/debug.hh>
#include <hpp/pinocchio/configuration.hh>
#include <hpp/pinocchio/device.hh>
#include <hpp/pinocchio/joint.hh>

#include <hpp/constraints/affine-function.hh>

namespace hpp {
  namespace core {
    using boost::assign::list_of;
    using constraints::ConstantFunction;
    namespace {
        template <typename T>
	std::string numToStr (const T& v) {
 	  std::stringstream ss; ss<<v; return ss.str ();
	}

        DifferentiableFunctionPtr_t makeFunction (
            const LiegroupElement& lge, const std::string& name)
        {
          return DifferentiableFunctionPtr_t
            (new ConstantFunction (lge, 0, 0, "LockedJoint " + name));
        }
    }

    /// Copy object and return shared pointer to copy
    EquationPtr_t LockedJoint::copy () const
    {
      return createCopy (weak_.lock ());
    }

    LockedJointPtr_t LockedJoint::create (const JointPtr_t& joint,
					  const LiegroupElement& value)
    {
      LockedJoint* ptr = new LockedJoint (joint, value);
      LockedJointPtr_t shPtr (ptr);
      ptr->init (shPtr);
      return shPtr;
    }

    LockedJointPtr_t LockedJoint::create (const JointPtr_t& joint,
                                          const size_type index,
					  vectorIn_t value)
    {
      LockedJoint* ptr = new LockedJoint (joint, index, value);
      LockedJointPtr_t shPtr (ptr);
      ptr->init (shPtr);
      return shPtr;
    }

    LockedJointPtr_t LockedJoint::create (const DevicePtr_t& dev,
        const size_type index, vectorIn_t value)
    {
      LockedJoint* ptr = new LockedJoint (dev, index, value);
      LockedJointPtr_t shPtr (ptr);
      ptr->init (shPtr);
      return shPtr;
    }

    LockedJointPtr_t LockedJoint::createCopy (LockedJointConstPtr_t other)
    {
      LockedJoint* ptr = new LockedJoint (*other);
      LockedJointPtr_t shPtr (ptr);
      ptr->init (shPtr);
      return shPtr;
    }

    size_type LockedJoint::rankInConfiguration () const
    {
      return outputConf_[0].first;
    }

    size_type LockedJoint::rankInVelocity () const
    {
      return outputVelocity_[0].first;
    }

    size_type LockedJoint::configSize () const
    {
      return configSpace_->nq ();
    }

    size_type LockedJoint::numberDof () const
    {
      return configSpace_->nv ();
    }

    const LiegroupSpacePtr_t& LockedJoint::configSpace () const
    {
      return configSpace_;
    }

    bool LockedJoint::isSatisfied (ConfigurationIn_t config)
    {
      vector_t error;
      return isSatisfied (config, error);
    }

    bool LockedJoint::isSatisfied (ConfigurationIn_t config, vector_t& error)
    {
      LiegroupElement q (config.segment (rankInConfiguration(), configSize ()),
                         configSpace_);
      error = q - (configSpace_->exp (rightHandSide ()));
      return error.isApprox (vector_t::Zero (joint_->numberDof ()));
    }

    void LockedJoint::rightHandSideFromConfig (ConfigurationIn_t config)
    {
      if (!constantRightHandSide ()) {
        LiegroupElement q (config.segment (rankInConfiguration(), configSize ()),
                           configSpace_);
        rightHandSide (q - configSpace_->neutral ());
      }
    }

    LockedJoint::LockedJoint (const JointPtr_t& joint,
                              const LiegroupElement& value) :
      ExplicitNumericalConstraint (
          joint->robot(),
          makeFunction (value, joint->name()),
          segments_t(), // input conf
          segments_t(), // input vel
          list_of(segment_t (joint->rankInConfiguration(), joint->configSize())), // output conf
          list_of(segment_t (joint->rankInVelocity     (), joint->numberDof ()))  // output vel
          ),
      jointName_ (joint->name ()),
      configSpace_ (joint->configurationSpace ())
    {
      assert (rhsSize () == joint->numberDof ());
      assert (*(value.space ()) == *configSpace_);
      // rightHandSide (value - configSpace_->neutral ());
    }

    LockedJoint::LockedJoint (const JointPtr_t& joint, const size_type index,
        vectorIn_t value) :
      ExplicitNumericalConstraint (
          joint->robot(),
          makeFunction (
            LiegroupElement (value, LiegroupSpace::Rn (joint->configSize () - index)),
            "partial_" + joint->name()),
          segments_t(), // input conf
          segments_t(), // input vel
          list_of(segment_t (joint->rankInConfiguration(), joint->configSize()-index)), // output conf
          list_of(segment_t (joint->rankInVelocity     (), joint->numberDof ()-index))  // output vel
          ),
      jointName_ ("partial_" + joint->name ()),
      joint_ (joint),
      configSpace_ (LiegroupSpace::Rn (joint->configSize () - index))
    {
      assert (joint->numberDof () == joint->configSize ());
      // rightHandSide (value);
      assert (rhsSize () == value.size());
    }

    LockedJoint::LockedJoint (const DevicePtr_t& dev, const size_type index,
        vectorIn_t value) :
      ExplicitNumericalConstraint (
          dev,
          makeFunction (
            LiegroupElement (value, LiegroupSpace::Rn (value.size ())),
            dev->name() + "_extraDof" + numToStr (index)),
          segments_t(), // input conf
          segments_t(), // input vel
          list_of(segment_t (
              dev->configSize () - dev->extraConfigSpace().dimension() + index,
              value.size())), // output conf
          list_of(segment_t (
              dev->numberDof ()  - dev->extraConfigSpace().dimension() + index,
              value.size()))  // output vel
          ),
      jointName_ (dev->name() + "_extraDof" + numToStr (index)),
      joint_ (JointPtr_t ()), configSpace_ (LiegroupSpace::Rn (value.size ()))
    {
      assert (value.size() > 0);
      assert (rankInConfiguration() + value.size() <= dev->configSize());
      // rightHandSide (value);
      assert (rhsSize () == value.size());
    }

    void LockedJoint::init (const LockedJointPtr_t& self)
    {
      ExplicitNumericalConstraint::init(self);
      weak_ = self;
    }

    std::ostream& LockedJoint::print (std::ostream& os) const
    {
      LiegroupElement v; vector_t empty;
      function().value(v, empty);
      v += rightHandSide();
      os << "Locked joint " << jointName_
	 << ", value = " << pinocchio::displayConfig (v.vector())
        << ": rank in configuration = " << rankInConfiguration()
        << ": rank in velocity = " << rankInVelocity()
        << std::endl;
      return os;
    }

    LockedJoint::LockedJoint (const LockedJoint& other) :
      ExplicitNumericalConstraint (other), jointName_ (other.jointName_),
      joint_ (other.joint_), configSpace_ (other.configSpace_), weak_ ()
    {
    }

    bool LockedJoint::isEqual (const Equation& other, bool swapAndTest) const
    {
      try {
	const LockedJoint& lj =
	  dynamic_cast <const LockedJoint&> (other);
	if (!Equation::isEqual (other, false)) return false;
	if (jointName_ != lj.jointName_) return false;
	if (rankInConfiguration() != lj.rankInConfiguration()) return false;
	if (rankInVelocity() != lj.rankInVelocity()) return false;
	if (*configSpace_ != *(lj.configSpace_)) return false;
	if (swapAndTest) return lj.isEqual (*this, false);
	return true;
      } catch (const std::bad_cast& err) {
	return false;
      }
    }
  } // namespace core
} // namespace hpp
<|endoftext|>


<commit_before>/* BEGIN_COMMON_COPYRIGHT_HEADER
 * (c)LGPL2+
 *
 * LXQt - a lightweight, Qt based, desktop toolset
 * https://lxqt.org
 *
 * Copyright: 2011 Razor team
 * Authors:
 *   Petr Vanek <petr@scribus.info>
 *
 * This program or library is free software; you can redistribute it
 * and/or modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * 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., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301 USA
 *
 * END_COMMON_COPYRIGHT_HEADER */

#include <QButtonGroup>
#include <QWheelEvent>
#include <QtDebug>
#include <QSignalMapper>
#include <QTimer>
#include <lxqt-globalkeys.h>
#include <LXQt/GridLayout>
#include <KWindowSystem/KWindowSystem>
#include <QX11Info>
#include <cmath>

#include "desktopswitch.h"
#include "desktopswitchbutton.h"
#include "desktopswitchconfiguration.h"

static const QString DEFAULT_SHORTCUT_TEMPLATE(QStringLiteral("Control+F%1"));

DesktopSwitch::DesktopSwitch(const ILXQtPanelPluginStartupInfo &startupInfo) :
    QObject(),
    ILXQtPanelPlugin(startupInfo),
    m_pSignalMapper(new QSignalMapper(this)),
    m_desktopCount(KWindowSystem::numberOfDesktops()),
    mRows(-1),
    mShowOnlyActive(false),
    mDesktops(new NETRootInfo(QX11Info::connection(), NET::NumberOfDesktops | NET::CurrentDesktop | NET::DesktopNames, NET::WM2DesktopLayout)),
    mLabelType(static_cast<DesktopSwitchButton::LabelType>(-1))
{
    m_buttons = new QButtonGroup(this);
    connect (m_pSignalMapper, SIGNAL(mapped(int)), this, SLOT(setDesktop(int)));

    mLayout = new LXQt::GridLayout(&mWidget);
    mWidget.setLayout(mLayout);

    settingsChanged();

    onCurrentDesktopChanged(KWindowSystem::currentDesktop());
    QTimer::singleShot(0, this, SLOT(registerShortcuts()));

    connect(m_buttons, SIGNAL(buttonClicked(int)), this, SLOT(setDesktop(int)));

    connect(KWindowSystem::self(), SIGNAL(numberOfDesktopsChanged(int)), SLOT(onNumberOfDesktopsChanged(int)));
    connect(KWindowSystem::self(), SIGNAL(currentDesktopChanged(int)), SLOT(onCurrentDesktopChanged(int)));
    connect(KWindowSystem::self(), SIGNAL(desktopNamesChanged()), SLOT(onDesktopNamesChanged()));

    connect(KWindowSystem::self(), static_cast<void (KWindowSystem::*)(WId, NET::Properties, NET::Properties2)>(&KWindowSystem::windowChanged),
            this, &DesktopSwitch::onWindowChanged);
}

void DesktopSwitch::registerShortcuts()
{
    // Register shortcuts to change desktop
    GlobalKeyShortcut::Action * gshortcut;
    QString path;
    QString description;
    for (int i = 0; i < 12; ++i)
    {
        path = QStringLiteral("/panel/%1/desktop_%2").arg(settings()->group()).arg(i + 1);
        description = tr("Switch to desktop %1").arg(i + 1);

        gshortcut = GlobalKeyShortcut::Client::instance()->addAction(QString(), path, description, this);
        if (nullptr != gshortcut)
        {
            m_keys << gshortcut;
            connect(gshortcut, &GlobalKeyShortcut::Action::registrationFinished, this, &DesktopSwitch::shortcutRegistered);
            connect(gshortcut, SIGNAL(activated()), m_pSignalMapper, SLOT(map()));
            m_pSignalMapper->setMapping(gshortcut, i);
        }
    }
}

void DesktopSwitch::shortcutRegistered()
{
    GlobalKeyShortcut::Action * const shortcut = qobject_cast<GlobalKeyShortcut::Action*>(sender());

    disconnect(shortcut, &GlobalKeyShortcut::Action::registrationFinished, this, &DesktopSwitch::shortcutRegistered);

    const int i = m_keys.indexOf(shortcut);
    Q_ASSERT(-1 != i);

    if (shortcut->shortcut().isEmpty())
    {
        shortcut->changeShortcut(DEFAULT_SHORTCUT_TEMPLATE.arg(i + 1));
    }
}

void DesktopSwitch::onWindowChanged(WId id, NET::Properties properties, NET::Properties2 /*properties2*/)
{
    if (properties.testFlag(NET::WMState) && isWindowHighlightable(id))
    {
        KWindowInfo info = KWindowInfo(id, NET::WMDesktop | NET::WMState);
        int desktop = info.desktop();
        if (!info.valid() || info.onAllDesktops())
            return;
        else
        {
            DesktopSwitchButton *button = static_cast<DesktopSwitchButton *>(m_buttons->button(desktop - 1));
            if(button)
                button->setUrgencyHint(id, info.hasState(NET::DemandsAttention));
        }
    }
}

void DesktopSwitch::refresh()
{
    const QList<QAbstractButton*> btns = m_buttons->buttons();

    int i = 0;
    const int current_desktop = KWindowSystem::currentDesktop();
    const int current_cnt = btns.count();
    const int border = qMin(btns.count(), m_desktopCount);
    //update existing buttons
    for ( ; i < border; ++i)
    {
        DesktopSwitchButton * button = qobject_cast<DesktopSwitchButton*>(btns[i]);
        button->update(i, mLabelType,
                       KWindowSystem::desktopName(i + 1).isEmpty() ?
                       tr("Desktop %1").arg(i + 1) :
                       KWindowSystem::desktopName(i + 1));
        button->setVisible(!mShowOnlyActive || i + 1 == current_desktop);
    }

    //create new buttons (if neccessary)
    QAbstractButton *b;
    for ( ; i < m_desktopCount; ++i)
    {
        b = new DesktopSwitchButton(&mWidget, i, mLabelType,
                KWindowSystem::desktopName(i+1).isEmpty() ?
                tr("Desktop %1").arg(i+1) :
                KWindowSystem::desktopName(i+1));
        mWidget.layout()->addWidget(b);
        m_buttons->addButton(b, i);
        b->setVisible(!mShowOnlyActive || i + 1 == current_desktop);
    }

    //delete unneeded buttons (if neccessary)
    for ( ; i < current_cnt; ++i)
    {
        b = m_buttons->buttons().last();
        m_buttons->removeButton(b);
        mWidget.layout()->removeWidget(b);
        delete b;
    }
}

bool DesktopSwitch::isWindowHighlightable(WId window)
{
    // this method was borrowed from the taskbar plugin
    QFlags<NET::WindowTypeMask> ignoreList;
    ignoreList |= NET::DesktopMask;
    ignoreList |= NET::DockMask;
    ignoreList |= NET::SplashMask;
    ignoreList |= NET::ToolbarMask;
    ignoreList |= NET::MenuMask;
    ignoreList |= NET::PopupMenuMask;
    ignoreList |= NET::NotificationMask;

    KWindowInfo info(window, NET::WMWindowType | NET::WMState, NET::WM2TransientFor);
    if (!info.valid())
        return false;

    if (NET::typeMatchesMask(info.windowType(NET::AllTypesMask), ignoreList))
        return false;

    if (info.state() & NET::SkipTaskbar)
        return false;

    // WM_TRANSIENT_FOR hint not set - normal window
    WId transFor = info.transientFor();
    if (transFor == 0 || transFor == window || transFor == (WId) QX11Info::appRootWindow())
        return true;

    info = KWindowInfo(transFor, NET::WMWindowType);

    QFlags<NET::WindowTypeMask> normalFlag;
    normalFlag |= NET::NormalMask;
    normalFlag |= NET::DialogMask;
    normalFlag |= NET::UtilityMask;

    return !NET::typeMatchesMask(info.windowType(NET::AllTypesMask), normalFlag);
}

DesktopSwitch::~DesktopSwitch()
{
}

void DesktopSwitch::setDesktop(int desktop)
{
    KWindowSystem::setCurrentDesktop(desktop + 1);
}

void DesktopSwitch::onNumberOfDesktopsChanged(int count)
{
    qDebug() << "Desktop count changed from" << m_desktopCount << "to" << count;
    m_desktopCount = count;
    refresh();
}

void DesktopSwitch::onCurrentDesktopChanged(int current)
{
    if (mShowOnlyActive)
    {
        int i = 1;
        for (auto button : m_buttons->buttons())
        {
            if (current == i)
            {
                button->setChecked(true);
                button->setVisible(true);
            } else
            {
                button->setVisible(false);
            }
            ++i;
        }
    } else
    {
        QAbstractButton *button = m_buttons->button(current - 1);
        if (button)
            button->setChecked(true);
    }
}

void DesktopSwitch::onDesktopNamesChanged()
{
    refresh();
}

void DesktopSwitch::settingsChanged()
{
    const int rows = settings()->value(QStringLiteral("rows"), 1).toInt();
    const bool show_only_active = settings()->value(QStringLiteral("showOnlyActive"), false).toBool();
    const int label_type = settings()->value(QStringLiteral("labelType"), DesktopSwitchButton::LABEL_TYPE_NUMBER).toInt();

    const bool need_realign = mRows != rows || show_only_active != mShowOnlyActive;
    const bool need_refresh = mLabelType != static_cast<DesktopSwitchButton::LabelType>(label_type) || show_only_active != mShowOnlyActive;

    mRows = rows;
    mShowOnlyActive = show_only_active;
    mLabelType = static_cast<DesktopSwitchButton::LabelType>(label_type);
    if (need_realign)
        realign();
    if (need_refresh)
        refresh();
}

void DesktopSwitch::realign()
{
    int columns = static_cast<int>(ceil(static_cast<float>(m_desktopCount) / mRows));
    mLayout->setEnabled(false);
    if (panel()->isHorizontal())
    {
        mLayout->setRowCount(mShowOnlyActive ? 1 : mRows);
        mLayout->setColumnCount(0);
        mDesktops->setDesktopLayout(NET::OrientationHorizontal, columns, mRows, mWidget.isRightToLeft() ? NET::DesktopLayoutCornerTopRight : NET::DesktopLayoutCornerTopLeft);
    }
    else
    {
        mLayout->setColumnCount(mShowOnlyActive ? 1 : mRows);
        mLayout->setRowCount(0);
        mDesktops->setDesktopLayout(NET::OrientationHorizontal, mRows, columns, mWidget.isRightToLeft() ? NET::DesktopLayoutCornerTopRight : NET::DesktopLayoutCornerTopLeft);
    }
    mLayout->setEnabled(true);
}

QDialog *DesktopSwitch::configureDialog()
{
    return new DesktopSwitchConfiguration(settings());
}

DesktopSwitchWidget::DesktopSwitchWidget():
    QFrame(),
    m_mouseWheelThresholdCounter(0)
{
}

void DesktopSwitchWidget::wheelEvent(QWheelEvent *e)
{
    // Without some sort of threshold which has to be passed, scrolling is too sensitive
    m_mouseWheelThresholdCounter -= e->delta();
    // If the user hasn't scrolled far enough in one direction (positive or negative): do nothing
    if(abs(m_mouseWheelThresholdCounter) < 100)
        return;

    int max = KWindowSystem::numberOfDesktops();
    int delta = e->delta() < 0 ? 1 : -1;
    int current = KWindowSystem::currentDesktop() + delta;

    if (current > max){
        current = 1;
    }
    else if (current < 1)
        current = max;

    m_mouseWheelThresholdCounter = 0;
    KWindowSystem::setCurrentDesktop(current);
}
<commit_msg>Fixed high CPU usage of Desktop Switch<commit_after>/* BEGIN_COMMON_COPYRIGHT_HEADER
 * (c)LGPL2+
 *
 * LXQt - a lightweight, Qt based, desktop toolset
 * https://lxqt.org
 *
 * Copyright: 2011 Razor team
 * Authors:
 *   Petr Vanek <petr@scribus.info>
 *
 * This program or library is free software; you can redistribute it
 * and/or modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * 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., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301 USA
 *
 * END_COMMON_COPYRIGHT_HEADER */

#include <QButtonGroup>
#include <QWheelEvent>
#include <QtDebug>
#include <QSignalMapper>
#include <QTimer>
#include <lxqt-globalkeys.h>
#include <LXQt/GridLayout>
#include <KWindowSystem/KWindowSystem>
#include <QX11Info>
#include <cmath>

#include "desktopswitch.h"
#include "desktopswitchbutton.h"
#include "desktopswitchconfiguration.h"

static const QString DEFAULT_SHORTCUT_TEMPLATE(QStringLiteral("Control+F%1"));

DesktopSwitch::DesktopSwitch(const ILXQtPanelPluginStartupInfo &startupInfo) :
    QObject(),
    ILXQtPanelPlugin(startupInfo),
    m_pSignalMapper(new QSignalMapper(this)),
    m_desktopCount(KWindowSystem::numberOfDesktops()),
    mRows(-1),
    mShowOnlyActive(false),
    mDesktops(new NETRootInfo(QX11Info::connection(), NET::NumberOfDesktops | NET::CurrentDesktop | NET::DesktopNames, NET::WM2DesktopLayout)),
    mLabelType(static_cast<DesktopSwitchButton::LabelType>(-1))
{
    m_buttons = new QButtonGroup(this);
    connect (m_pSignalMapper, SIGNAL(mapped(int)), this, SLOT(setDesktop(int)));

    mLayout = new LXQt::GridLayout(&mWidget);
    mWidget.setLayout(mLayout);

    settingsChanged();

    onCurrentDesktopChanged(KWindowSystem::currentDesktop());
    QTimer::singleShot(0, this, SLOT(registerShortcuts()));

    connect(m_buttons, SIGNAL(buttonClicked(int)), this, SLOT(setDesktop(int)));

    connect(KWindowSystem::self(), SIGNAL(numberOfDesktopsChanged(int)), SLOT(onNumberOfDesktopsChanged(int)));
    connect(KWindowSystem::self(), SIGNAL(currentDesktopChanged(int)), SLOT(onCurrentDesktopChanged(int)));
    connect(KWindowSystem::self(), SIGNAL(desktopNamesChanged()), SLOT(onDesktopNamesChanged()));

    connect(KWindowSystem::self(), static_cast<void (KWindowSystem::*)(WId, NET::Properties, NET::Properties2)>(&KWindowSystem::windowChanged),
            this, &DesktopSwitch::onWindowChanged);
}

void DesktopSwitch::registerShortcuts()
{
    // Register shortcuts to change desktop
    GlobalKeyShortcut::Action * gshortcut;
    QString path;
    QString description;
    for (int i = 0; i < 12; ++i)
    {
        path = QStringLiteral("/panel/%1/desktop_%2").arg(settings()->group()).arg(i + 1);
        description = tr("Switch to desktop %1").arg(i + 1);

        gshortcut = GlobalKeyShortcut::Client::instance()->addAction(QString(), path, description, this);
        if (nullptr != gshortcut)
        {
            m_keys << gshortcut;
            connect(gshortcut, &GlobalKeyShortcut::Action::registrationFinished, this, &DesktopSwitch::shortcutRegistered);
            connect(gshortcut, SIGNAL(activated()), m_pSignalMapper, SLOT(map()));
            m_pSignalMapper->setMapping(gshortcut, i);
        }
    }
}

void DesktopSwitch::shortcutRegistered()
{
    GlobalKeyShortcut::Action * const shortcut = qobject_cast<GlobalKeyShortcut::Action*>(sender());

    disconnect(shortcut, &GlobalKeyShortcut::Action::registrationFinished, this, &DesktopSwitch::shortcutRegistered);

    const int i = m_keys.indexOf(shortcut);
    Q_ASSERT(-1 != i);

    if (shortcut->shortcut().isEmpty())
    {
        shortcut->changeShortcut(DEFAULT_SHORTCUT_TEMPLATE.arg(i + 1));
    }
}

void DesktopSwitch::onWindowChanged(WId id, NET::Properties properties, NET::Properties2 /*properties2*/)
{
    if (properties.testFlag(NET::WMState) && isWindowHighlightable(id))
    {
        KWindowInfo info = KWindowInfo(id, NET::WMDesktop | NET::WMState);
        int desktop = info.desktop();
        if (!info.valid() || info.onAllDesktops())
            return;
        else
        {
            DesktopSwitchButton *button = static_cast<DesktopSwitchButton *>(m_buttons->button(desktop - 1));
            if(button)
                button->setUrgencyHint(id, info.hasState(NET::DemandsAttention));
        }
    }
}

void DesktopSwitch::refresh()
{
    const QList<QAbstractButton*> btns = m_buttons->buttons();

    int i = 0;
    const int current_desktop = KWindowSystem::currentDesktop();
    const int current_cnt = btns.count();
    const int border = qMin(btns.count(), m_desktopCount);
    //update existing buttons
    for ( ; i < border; ++i)
    {
        DesktopSwitchButton * button = qobject_cast<DesktopSwitchButton*>(btns[i]);
        button->update(i, mLabelType,
                       KWindowSystem::desktopName(i + 1).isEmpty() ?
                       tr("Desktop %1").arg(i + 1) :
                       KWindowSystem::desktopName(i + 1));
        button->setVisible(!mShowOnlyActive || i + 1 == current_desktop);
    }

    //create new buttons (if neccessary)
    QAbstractButton *b;
    for ( ; i < m_desktopCount; ++i)
    {
        b = new DesktopSwitchButton(&mWidget, i, mLabelType,
                KWindowSystem::desktopName(i+1).isEmpty() ?
                tr("Desktop %1").arg(i+1) :
                KWindowSystem::desktopName(i+1));
        mWidget.layout()->addWidget(b);
        m_buttons->addButton(b, i);
        b->setVisible(!mShowOnlyActive || i + 1 == current_desktop);
    }

    //delete unneeded buttons (if neccessary)
    for ( ; i < current_cnt; ++i)
    {
        b = m_buttons->buttons().last();
        m_buttons->removeButton(b);
        mWidget.layout()->removeWidget(b);
        delete b;
    }
}

bool DesktopSwitch::isWindowHighlightable(WId window)
{
    // this method was borrowed from the taskbar plugin
    QFlags<NET::WindowTypeMask> ignoreList;
    ignoreList |= NET::DesktopMask;
    ignoreList |= NET::DockMask;
    ignoreList |= NET::SplashMask;
    ignoreList |= NET::ToolbarMask;
    ignoreList |= NET::MenuMask;
    ignoreList |= NET::PopupMenuMask;
    ignoreList |= NET::NotificationMask;

    KWindowInfo info(window, NET::WMWindowType | NET::WMState, NET::WM2TransientFor);
    if (!info.valid())
        return false;

    if (NET::typeMatchesMask(info.windowType(NET::AllTypesMask), ignoreList))
        return false;

    if (info.state() & NET::SkipTaskbar)
        return false;

    // WM_TRANSIENT_FOR hint not set - normal window
    WId transFor = info.transientFor();
    if (transFor == 0 || transFor == window || transFor == (WId) QX11Info::appRootWindow())
        return true;

    info = KWindowInfo(transFor, NET::WMWindowType);

    QFlags<NET::WindowTypeMask> normalFlag;
    normalFlag |= NET::NormalMask;
    normalFlag |= NET::DialogMask;
    normalFlag |= NET::UtilityMask;

    return !NET::typeMatchesMask(info.windowType(NET::AllTypesMask), normalFlag);
}

DesktopSwitch::~DesktopSwitch()
{
}

void DesktopSwitch::setDesktop(int desktop)
{
    KWindowSystem::setCurrentDesktop(desktop + 1);
}

void DesktopSwitch::onNumberOfDesktopsChanged(int count)
{
    qDebug() << "Desktop count changed from" << m_desktopCount << "to" << count;
    m_desktopCount = count;
    refresh();
}

void DesktopSwitch::onCurrentDesktopChanged(int current)
{
    if (mShowOnlyActive)
    {
        int i = 1;
        for (auto button : m_buttons->buttons())
        {
            if (current == i)
            {
                button->setChecked(true);
                button->setVisible(true);
            } else
            {
                button->setVisible(false);
            }
            ++i;
        }
    } else
    {
        QAbstractButton *button = m_buttons->button(current - 1);
        if (button)
            button->setChecked(true);
    }
}

void DesktopSwitch::onDesktopNamesChanged()
{
    refresh();
}

void DesktopSwitch::settingsChanged()
{
    const int rows = settings()->value(QStringLiteral("rows"), 1).toInt();
    const bool show_only_active = settings()->value(QStringLiteral("showOnlyActive"), false).toBool();
    const int label_type = settings()->value(QStringLiteral("labelType"), DesktopSwitchButton::LABEL_TYPE_NUMBER).toInt();

    const bool need_realign = mRows != rows || show_only_active != mShowOnlyActive;
    const bool need_refresh = mLabelType != static_cast<DesktopSwitchButton::LabelType>(label_type) || show_only_active != mShowOnlyActive;

    mRows = rows;
    mShowOnlyActive = show_only_active;
    mLabelType = static_cast<DesktopSwitchButton::LabelType>(label_type);
    if (need_realign)
    {
        // WARNING: Changing the desktop layout may call "LXQtPanel::realign", which calls
        // "DesktopSwitch::realign()". Therefore, the desktop layout should not be changed
        // inside the latter method.
        int columns = static_cast<int>(ceil(static_cast<float>(m_desktopCount) / mRows));
        if (panel()->isHorizontal())
        {
            mDesktops->setDesktopLayout(NET::OrientationHorizontal, columns, mRows, mWidget.isRightToLeft() ? NET::DesktopLayoutCornerTopRight : NET::DesktopLayoutCornerTopLeft);
        }
        else
        {
            mDesktops->setDesktopLayout(NET::OrientationHorizontal, mRows, columns, mWidget.isRightToLeft() ? NET::DesktopLayoutCornerTopRight : NET::DesktopLayoutCornerTopLeft);
        }
        realign(); // in case it isn't called when the desktop layout changes
    }
    if (need_refresh)
        refresh();
}

void DesktopSwitch::realign()
{
    mLayout->setEnabled(false);
    if (panel()->isHorizontal())
    {
        mLayout->setRowCount(mShowOnlyActive ? 1 : mRows);
        mLayout->setColumnCount(0);
    }
    else
    {
        mLayout->setColumnCount(mShowOnlyActive ? 1 : mRows);
        mLayout->setRowCount(0);
    }
    mLayout->setEnabled(true);
}

QDialog *DesktopSwitch::configureDialog()
{
    return new DesktopSwitchConfiguration(settings());
}

DesktopSwitchWidget::DesktopSwitchWidget():
    QFrame(),
    m_mouseWheelThresholdCounter(0)
{
}

void DesktopSwitchWidget::wheelEvent(QWheelEvent *e)
{
    // Without some sort of threshold which has to be passed, scrolling is too sensitive
    m_mouseWheelThresholdCounter -= e->delta();
    // If the user hasn't scrolled far enough in one direction (positive or negative): do nothing
    if(abs(m_mouseWheelThresholdCounter) < 100)
        return;

    int max = KWindowSystem::numberOfDesktops();
    int delta = e->delta() < 0 ? 1 : -1;
    int current = KWindowSystem::currentDesktop() + delta;

    if (current > max){
        current = 1;
    }
    else if (current < 1)
        current = max;

    m_mouseWheelThresholdCounter = 0;
    KWindowSystem::setCurrentDesktop(current);
}
<|endoftext|>


<commit_before>#include "stdlib.h"
#include "string.h"
#include "stdarg.h"
#include "mysql.h"
#include "v8.h"

#define TRUE                            1
#define FALSE                           0
#define JS_MAX_RETURN_VALUE_LENGTH      65535

#define MSG_MISSING_SCRIPT              "Missing script argument."
#define MSG_SCRIPT_MUST_BE_STRING       "Script argument must be a string."
#define MSG_RESOURCE_ALLOCATION_FAILED  "Failed to allocate v8 resources."
#define MSG_CREATE_CONTEXT_FAILED       "Failed to create context."
#define MSG_SCRIPT_COMPILATION_FAILED   "Error compiling script."

#define LOG_ERR(a) fprintf(stderr, "\n%s", a);

#define INIT_ERROR                      1
#define INIT_SUCCESS                    0

char* getExceptionString(v8::TryCatch* try_catch) {
  v8::String::AsciiValue exception(try_catch->Exception());
  return *exception;
}

v8::Handle<v8::Value> Log(const v8::Arguments& args) {
  v8::Handle<v8::Value> value = args[0];
  v8::String::AsciiValue ascii(value);
  fprintf(stderr, "\n%s", *ascii);
  return v8::Undefined();
}

v8::Handle<v8::ObjectTemplate> getConsoleTemplate(){
  v8::Handle<v8::ObjectTemplate> console = v8::ObjectTemplate::New();
  console->Set(v8::String::New("log"), v8::FunctionTemplate::New(Log));
  return console;
}

v8::Handle<v8::Value> getBuiltinConsole(
  v8::Local<v8::String> property,
  const v8::AccessorInfo& info
) {
  return getConsoleTemplate()->NewInstance();
}

v8::Handle<v8::ObjectTemplate> getGlobalTemplate(){
  v8::Handle<v8::ObjectTemplate> global = v8::ObjectTemplate::New();
  global->SetAccessor(v8::String::New("console"), getBuiltinConsole);
  return global;
}

//ARG_EXTRACTOR = pointer to an extractor function
typedef v8::Handle<v8::Value> (*ARG_EXTRACTOR)(UDF_ARGS*, unsigned int);
typedef v8::Handle<v8::Value> (**ARRAY_OF_ARG_EXTRACTORS)(UDF_ARGS*, unsigned int);

typedef struct st_v8_resources {
  v8::Persistent<v8::Context> context;
  v8::Persistent<v8::Script> script;
  v8::Persistent<v8::Array> arguments;
  ARG_EXTRACTOR *arg_extractors;
  my_bool compiled;
  char *result;
  unsigned long long max_result_length;
} V8RES;

v8::Handle<v8::String> getStringArgString(
  UDF_ARGS *args,
  unsigned int i
){
  return v8::String::New(
    args->args[i],
    args->lengths[i]
  );
}

v8::Handle<v8::Value> getStringArgValue(
  UDF_ARGS *args,
  unsigned int i
){
  return v8::String::New(
    args->args[i],
    args->lengths[i]
  );
}

v8::Handle<v8::Value> getIntArgValue(
  UDF_ARGS *args,
  unsigned int i
){
  long long int_val = *((long long *)args->args[i]);
  if (int_val >> 31) return v8::Number::New((double)int_val);
  else return v8::Integer::New(int_val);
}

v8::Handle<v8::Value> getRealArgValue(
  UDF_ARGS *args,
  unsigned int i
){
  return v8::Number::New(*((double *)args->args[i]));
}

void setupArguments(V8RES *v8res, UDF_ARGS* args) {
  //set up arguments.
  //Any arguments beyond the initial "script" argument
  //are available in a global array called arguments
  v8::Local<v8::Array> arguments = v8::Array::New(args->arg_count - 1);
  v8res->context->Global()->Set(
    v8::String::New("arguments"),
    arguments
  );
  v8res->arguments = v8::Persistent<v8::Array>::New(arguments);
  for (unsigned int i = 1; i < args->arg_count; i++) {
    //for each argument, find a suitable extractor.
    //(the extractor translates value from mysql to v8 land)
    ARG_EXTRACTOR arg_extractor;
    switch (args->arg_type[i]) {
      case DECIMAL_RESULT:
      case ROW_RESULT:
      case STRING_RESULT:
        arg_extractor = getStringArgValue;
        break;
      case INT_RESULT:
        arg_extractor = getIntArgValue;
        break;
      case REAL_RESULT:
        arg_extractor = getRealArgValue;
        break;
    }
    if (args->args[i] == NULL) {
      //this is a non-constant argument.
      //store the extractor so we can call it in the row-level function
      v8res->arg_extractors[i] = arg_extractor;
    }
    else {
      //this is a constant argument.
      //call the extractor only once here to obtain its value
      v8res->arguments->Set(i - 1, (*arg_extractor)(args, i));
      v8res->arg_extractors[i] = NULL;
    }
  }
}

void assignArguments(V8RES *v8res, UDF_ARGS* args) {
  ARG_EXTRACTOR arg_extractor;
  v8::Handle<v8::Value> val;
  for (unsigned int i = 1; i < args->arg_count; i++) {
    arg_extractor = v8res->arg_extractors[i];
    //if this is a constant argument, the value is already set
    //so we can skip it.
    if (arg_extractor == NULL) continue;
    //extract and store the argument value
    if (args->args[i] == NULL) val = v8::Null();
    else val = (*arg_extractor)(args, i);
    v8res->arguments->Set(i - 1, val);
  }
}

#ifdef __cplusplus
extern "C" {
#endif

my_bool js_init(
  UDF_INIT *initid,
  UDF_ARGS *args,
  char *message
){
  if (args->arg_count < 1) {
    strcpy(message, MSG_MISSING_SCRIPT);
    return INIT_ERROR;
  }
  if (args->arg_type[0] != STRING_RESULT) {
    strcpy(message, MSG_SCRIPT_MUST_BE_STRING);
    return INIT_ERROR;
  }
  initid->ptr = (char *)malloc(sizeof(V8RES));
  if (initid->ptr == NULL) {
    strcpy(message, MSG_RESOURCE_ALLOCATION_FAILED);
    return INIT_ERROR;
  }
  V8RES *v8res = (V8RES *)initid->ptr;
  v8res->result = NULL;
  v8res->max_result_length = 0;
  v8res->arg_extractors = (ARG_EXTRACTOR*)malloc(
    args->arg_count * sizeof(ARG_EXTRACTOR)
  );
  if (v8res->arg_extractors == NULL) {
    strcpy(message, MSG_RESOURCE_ALLOCATION_FAILED);
    return INIT_ERROR;
  }
  //v8 introductory voodoo incantations
  v8::Locker locker;
  v8::TryCatch try_catch;
  v8::HandleScope handle_scope;

  //set up a context
  v8res->context = v8::Context::New(NULL, getGlobalTemplate());
  if (v8res->context.IsEmpty()) {
    strcpy(message, MSG_CREATE_CONTEXT_FAILED);
    return INIT_ERROR;
  }
  v8res->context->Enter();

  setupArguments(v8res, args);

  //check if we can pre-compile the script
  if (args->args[0] == FALSE) { //script argument is not a constant.
    v8res->compiled = FALSE;
  }
  else {                    //script argument is a constant, compile it.
    v8res->script = v8::Persistent<v8::Script>::New(
      v8::Script::New(getStringArgString(args, 0))
    );
    if (v8res->script.IsEmpty()) {
      char *exceptionMessage = getExceptionString(&try_catch);
      strcpy(message, exceptionMessage);
      LOG_ERR(MSG_SCRIPT_COMPILATION_FAILED);
      LOG_ERR(exceptionMessage);
      return INIT_ERROR;
    }
    v8res->compiled = TRUE;
  }

  initid->max_length = JS_MAX_RETURN_VALUE_LENGTH;
  v8res->context->Exit();
  return INIT_SUCCESS;
}

void js_deinit(
  UDF_INIT *initid
){
  if (initid->ptr == NULL) return;
  //clean up v8
  V8RES *v8res = (V8RES *)initid->ptr;
  if (v8res->compiled == TRUE) {
    v8res->script.Dispose();
  }
  v8res->arguments.Dispose();
  v8res->context.Dispose();
  v8res->context.Clear();
  if (v8res->result != NULL) free(v8res->result);
  free(v8res->arg_extractors);
  free(v8res);
}

char *js(
  UDF_INIT *initid,
  UDF_ARGS *args,
  char *result,
  unsigned long *length,
  my_bool *is_null,
  my_bool *error
){
  v8::Locker locker;
  v8::TryCatch try_catch;
  V8RES *v8res = (V8RES *)initid->ptr;
  v8res->context->Enter();
  v8::Context::Scope context_scope(v8res->context);
  v8::HandleScope handle_scope;

  //assign argument array entries for this row;
  assignArguments(v8res, args);

  //get script to execute
  v8::Handle<v8::Script> script;
  if (v8res->compiled == TRUE){ //already compiled.
    script = v8res->script;
  }
  else {                        //compile on the fly
    script = v8::Script::Compile(getStringArgString(args, 0));
    if (script.IsEmpty()) {
      LOG_ERR(MSG_SCRIPT_COMPILATION_FAILED);
      *error = TRUE;
      v8res->context->Exit();
      return NULL;
    }
  }

  //execute and get value
  v8::Handle<v8::Value> value = script->Run();
  if (value.IsEmpty()){
    LOG_ERR(getExceptionString(&try_catch));
    *error = TRUE;
    v8res->context->Exit();
    return NULL;
  }

  //return the value returned by the script
  v8::String::AsciiValue ascii(value);
  *length = (unsigned long)ascii.length();
  if (*length > v8res->max_result_length) {
    if (v8res->result != NULL) free(v8res->result);
    v8res->result = (char *)malloc(*length);
    if (v8res->result == NULL) {
      v8res->max_result_length = 0;
      LOG_ERR(MSG_RESOURCE_ALLOCATION_FAILED);
      *error = TRUE;
      v8res->context->Exit();
      return NULL;
    }
    else v8res->max_result_length = *length;
  }
  strcpy(v8res->result, *ascii);
  v8res->context->Exit();
  return v8res->result;
}

#ifdef __cplusplus
};
#endif
<commit_msg>Minor optimization / refactoring.<commit_after>#include "stdlib.h"
#include "string.h"
#include "stdarg.h"
#include "mysql.h"
#include "v8.h"

#define TRUE                            1
#define FALSE                           0
#define JS_MAX_RETURN_VALUE_LENGTH      65535

#define MSG_MISSING_SCRIPT              "Missing script argument."
#define MSG_SCRIPT_MUST_BE_STRING       "Script argument must be a string."
#define MSG_RESOURCE_ALLOCATION_FAILED  "Failed to allocate v8 resources."
#define MSG_CREATE_CONTEXT_FAILED       "Failed to create context."
#define MSG_SCRIPT_COMPILATION_FAILED   "Error compiling script."

#define LOG_ERR(a) fprintf(stderr, "\n%s", a);

#define INIT_ERROR                      1
#define INIT_SUCCESS                    0

char* getExceptionString(v8::TryCatch* try_catch) {
  v8::String::AsciiValue exception(try_catch->Exception());
  return *exception;
}

v8::Handle<v8::Value> Log(const v8::Arguments& args) {
  v8::Handle<v8::Value> value = args[0];
  v8::String::AsciiValue ascii(value);
  fprintf(stderr, "\n%s", *ascii);
  return v8::Undefined();
}

v8::Handle<v8::ObjectTemplate> getConsoleTemplate(){
  v8::Handle<v8::ObjectTemplate> console = v8::ObjectTemplate::New();
  console->Set(v8::String::New("log"), v8::FunctionTemplate::New(Log));
  return console;
}

v8::Handle<v8::Value> getBuiltinConsole(
  v8::Local<v8::String> property,
  const v8::AccessorInfo& info
) {
  return getConsoleTemplate()->NewInstance();
}

v8::Handle<v8::ObjectTemplate> getGlobalTemplate(){
  v8::Handle<v8::ObjectTemplate> global = v8::ObjectTemplate::New();
  global->SetAccessor(v8::String::New("console"), getBuiltinConsole);
  return global;
}

//ARG_EXTRACTOR = pointer to an extractor function
typedef v8::Handle<v8::Value> (*ARG_EXTRACTOR)(UDF_ARGS*, unsigned int);
typedef v8::Handle<v8::Value> (**ARRAY_OF_ARG_EXTRACTORS)(UDF_ARGS*, unsigned int);

typedef struct st_v8_resources {
  v8::Persistent<v8::Context> context;
  v8::Persistent<v8::Script> script;
  v8::Persistent<v8::Array> arguments;
  ARG_EXTRACTOR *arg_extractors;
  my_bool compiled;
  char *result;
  unsigned long long max_result_length;
} V8RES;

v8::Handle<v8::String> getStringArgString(
  UDF_ARGS *args,
  unsigned int i
){
  return v8::String::New(
    args->args[i],
    args->lengths[i]
  );
}

v8::Handle<v8::Value> getStringArgValue(
  UDF_ARGS *args,
  unsigned int i
){
  return v8::String::New(
    args->args[i],
    args->lengths[i]
  );
}

v8::Handle<v8::Script> getScriptArgValue(
  UDF_ARGS *args,
  unsigned int i
){
  return v8::Script::Compile(getStringArgString(args, i));
}

v8::Handle<v8::Value> getIntArgValue(
  UDF_ARGS *args,
  unsigned int i
){
  long long int_val = *((long long *)args->args[i]);
  if (int_val >> 31) return v8::Number::New((double)int_val);
  else return v8::Integer::New(int_val);
}

v8::Handle<v8::Value> getRealArgValue(
  UDF_ARGS *args,
  unsigned int i
){
  return v8::Number::New(*((double *)args->args[i]));
}

void setupArguments(V8RES *v8res, UDF_ARGS* args) {
  //set up arguments.
  //Any arguments beyond the initial "script" argument
  //are available in a global array called arguments
  v8::Local<v8::Array> arguments = v8::Array::New(args->arg_count - 1);
  v8res->context->Global()->Set(
    v8::String::New("arguments"),
    arguments
  );
  v8res->arguments = v8::Persistent<v8::Array>::New(arguments);
  for (unsigned int i = 1; i < args->arg_count; i++) {
    //for each argument, find a suitable extractor.
    //(the extractor translates value from mysql to v8 land)
    ARG_EXTRACTOR arg_extractor;
    switch (args->arg_type[i]) {
      case DECIMAL_RESULT:
      case ROW_RESULT:
      case STRING_RESULT:
        arg_extractor = getStringArgValue;
        break;
      case INT_RESULT:
        arg_extractor = getIntArgValue;
        break;
      case REAL_RESULT:
        arg_extractor = getRealArgValue;
        break;
    }
    if (args->args[i] == NULL) {
      //this is a non-constant argument.
      //store the extractor so we can call it in the row-level function
      v8res->arg_extractors[i] = arg_extractor;
    }
    else {
      //this is a constant argument.
      //call the extractor only once here to obtain its value
      v8res->arguments->Set(i - 1, (*arg_extractor)(args, i));
      v8res->arg_extractors[i] = NULL;
    }
  }
}

void assignArguments(V8RES *v8res, UDF_ARGS* args) {
  ARG_EXTRACTOR arg_extractor;
  v8::Handle<v8::Value> val;
  for (unsigned int i = 1; i < args->arg_count; i++) {
    arg_extractor = v8res->arg_extractors[i];
    //if this is a constant argument, the value is already set
    //so we can skip it.
    if (arg_extractor == NULL) continue;
    //extract and store the argument value
    if (args->args[i] == NULL) val = v8::Null();
    else val = (*arg_extractor)(args, i);
    v8res->arguments->Set(i - 1, val);
  }
}

my_bool alloc_result(V8RES *v8res, unsigned long *length) {
  if (*length <= v8res->max_result_length) return TRUE;
  if (v8res->result != NULL) free(v8res->result);
  v8res->result = (char *)malloc(*length);
  if (v8res->result == NULL) {
    v8res->max_result_length = 0;
    LOG_ERR(MSG_RESOURCE_ALLOCATION_FAILED);
    return FALSE;
  }
  else v8res->max_result_length = *length;
  return TRUE;
}

#ifdef __cplusplus
extern "C" {
#endif

my_bool js_init(
  UDF_INIT *initid,
  UDF_ARGS *args,
  char *message
){
  //validate arguments
  if (args->arg_count < 1) {
    strcpy(message, MSG_MISSING_SCRIPT);
    return INIT_ERROR;
  }
  if (args->arg_type[0] != STRING_RESULT) {
    strcpy(message, MSG_SCRIPT_MUST_BE_STRING);
    return INIT_ERROR;
  }
  //allocate main resource
  initid->ptr = (char *)malloc(sizeof(V8RES));
  if (initid->ptr == NULL) {
    strcpy(message, MSG_RESOURCE_ALLOCATION_FAILED);
    return INIT_ERROR;
  }
  V8RES *v8res = (V8RES *)initid->ptr;
  v8res->result = NULL;
  v8res->max_result_length = 0;
  v8res->arg_extractors = (ARG_EXTRACTOR*)malloc(
    args->arg_count * sizeof(ARG_EXTRACTOR)
  );
  if (v8res->arg_extractors == NULL) {
    strcpy(message, MSG_RESOURCE_ALLOCATION_FAILED);
    return INIT_ERROR;
  }
  //v8 introductory voodoo incantations
  v8::Locker locker;
  v8::TryCatch try_catch;
  v8::HandleScope handle_scope;

  //set up a context
  v8res->context = v8::Context::New(NULL, getGlobalTemplate());
  if (v8res->context.IsEmpty()) {
    strcpy(message, MSG_CREATE_CONTEXT_FAILED);
    return INIT_ERROR;
  }
  v8res->context->Enter();

  //create and initialize arguments array
  setupArguments(v8res, args);

  //set some properties of the return value.
  initid->max_length = JS_MAX_RETURN_VALUE_LENGTH;
  //script author can return what they like, including null
  initid->maybe_null = TRUE;
  //script author can always write a non-deterministic script
  initid->const_item = FALSE;

  //check if we can pre-compile the script
  if (args->args[0] == FALSE) { //script argument is not a constant.
    v8res->compiled = FALSE;
  }
  else {                    //script argument is a constant, compile it.
    v8res->script = v8::Persistent<v8::Script>::New(
      getScriptArgValue(args, 0)
    );
    if (v8res->script.IsEmpty()) {
      char *exceptionMessage = getExceptionString(&try_catch);
      strcpy(message, exceptionMessage);
      LOG_ERR(MSG_SCRIPT_COMPILATION_FAILED);
      LOG_ERR(exceptionMessage);
      return INIT_ERROR;
    }
    v8res->compiled = TRUE;
  }

  //
  v8res->context->Exit();

  return INIT_SUCCESS;
}

void js_deinit(
  UDF_INIT *initid
){
  if (initid->ptr == NULL) return;
  //clean up v8
  V8RES *v8res = (V8RES *)initid->ptr;
  if (v8res->compiled == TRUE) {
    v8res->script.Dispose();
  }
  v8res->arguments.Dispose();
  v8res->context.Dispose();
  v8res->context.Clear();
  if (v8res->result != NULL) free(v8res->result);
  free(v8res->arg_extractors);
  free(v8res);
}

char *js(
  UDF_INIT *initid,
  UDF_ARGS *args,
  char *result,
  unsigned long *length,
  my_bool *is_null,
  my_bool *error
){
  //more sacrifices to the v8 deities.
  v8::Locker locker;
  v8::TryCatch try_catch;
  V8RES *v8res = (V8RES *)initid->ptr;
  v8res->context->Enter();
  v8::Context::Scope context_scope(v8res->context);
  v8::HandleScope handle_scope;

  //assign argument array entries for this row;
  assignArguments(v8res, args);

  //get script to execute
  v8::Handle<v8::Script> script;
  if (v8res->compiled == TRUE){ //already compiled.
    script = v8res->script;
  }
  else {                        //compile on the fly
    script = getScriptArgValue(args, 0);
    if (script.IsEmpty()) {
      LOG_ERR(MSG_SCRIPT_COMPILATION_FAILED);
      *error = TRUE;
      v8res->context->Exit();
      return NULL;
    }
  }

  //execute and get value
  v8::Handle<v8::Value> value = script->Run();
  if (value.IsEmpty()){
    LOG_ERR(getExceptionString(&try_catch));
    *error = TRUE;
    v8res->context->Exit();
    return NULL;
  }

  //return the value returned by the script
  v8::String::AsciiValue ascii(value);
  *length = (unsigned long)ascii.length();
  if (alloc_result(v8res, length) == FALSE) {
    *error = TRUE;
    return NULL;
  }
  strcpy(v8res->result, *ascii);
  v8res->context->Exit();
  return v8res->result;
}

#ifdef __cplusplus
};
#endif
<|endoftext|>


<commit_before>/*
-----------------------------------------------------------------------------

Copyright (c) 2010 Nigel Atkinson

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-----------------------------------------------------------------------------
*/
#include "luaresource.h"

/** @brief LuaResource
  *
  * @todo: document this function
  */
LuaResource::LuaResource(Ogre::ResourceManager* creator, const Ogre::String& name,
    Ogre::ResourceHandle handle, const Ogre::String& group ,
    bool isManual , Ogre::ManualResourceLoader* loader) :
    Ogre::Resource( creator, name, handle, group, isManual, loader )
{
    createParamDictionary("LuaResource");
}

/** @brief ~LuaResource
  *
  * @todo: document this function
  */
LuaResource::~LuaResource()
{
    unload();
}

/** @brief calculateSize
  *
  * @todo: document this function
  */
size_t LuaResource::calculateSize() const
{
    return mScriptSource.length();
}

/** @brief unloadImpl
  *
  * @todo: document this function
  */
void LuaResource::unloadImpl()
{
    mScriptSource.clear();
}

/** @brief loadImpl
  *
  * @todo: document this function
  */
void LuaResource::loadImpl()
{
    Ogre::DataStreamPtr stream = Ogre::ResourceGroupManager::getSingleton()
        .openResource( mName, mGroup, true, this );

    mScriptSource = stream->getAsString();
}

/** @brief getScriptSource
  *
  * @todo: document this function
  */
const Ogre::String LuaResource::getScriptSource() const
{
    return mScriptSource;
}

#if OGRE_VERSION < 67584 /* 1.8.0 */
template<>
LuaResourceManager* Ogre::Singleton<LuaResourceManager>::ms_Singleton = 0;
#else
template<>
LuaResourceManager* Ogre::Singleton<LuaResourceManager>::msSingleton = 0;
#endif

/** @brief LuaResourceManager
  *
  * @todo: document this function
  */
LuaResourceManager::LuaResourceManager()
{
    mResourceType = "LuaScript";
    mLoadOrder = 50.0f;

    // Register with OGRE
    Ogre::ResourceGroupManager::getSingleton()._registerResourceManager( mResourceType, this );
}

/** @brief ~LuaResourceManager
  *
  * @todo: document this function
  */
LuaResourceManager::~LuaResourceManager()
{
    Ogre::ResourceGroupManager::getSingleton()._unregisterResourceManager(mResourceType);
}

/** @brief createImpl
  *
  * @todo: document this function
  */
Ogre::Resource* LuaResourceManager::createImpl(const Ogre::String& name, Ogre::ResourceHandle handle, const Ogre::String& group, bool isManual, Ogre::ManualResourceLoader* loader, const Ogre::NameValuePairList* createParams)
{
    return new LuaResource( this, name, handle, group, isManual, loader );
}

/** @brief load
  *
  * @todo: document this function
  */
LuaResourcePtr LuaResourceManager::load(const Ogre::String& name, const Ogre::String& group)
{
    LuaResourcePtr script = getByName(name);

    if( script.isNull() )   // Not yet created
        script = create( name, group );

    script->load();

    return script;
}

/** @brief getSingeltonPtr
  *
  * @todo: document this function
  */

#if OGRE_VERSION < 67584 /* 1.8.0 */
LuaResourceManager * LuaResourceManager::getSingeltonPtr()
{
    return ms_Singleton;
}

/** @brief getSingleton
  *
  * @todo: document this function
  */
LuaResourceManager & LuaResourceManager::getSingleton()
{
    assert(ms_Singleton);
    return *ms_Singleton;
}
#else
LuaResourceManager * LuaResourceManager::getSingeltonPtr()
{
    return msSingleton;
}

/** @brief getSingleton
  *
  * @todo: document this function
  */
LuaResourceManager & LuaResourceManager::getSingleton()
{
    assert(msSingleton);
    return *msSingleton;
}
#endif

<commit_msg>Zap unmaintainted comments.<commit_after>/*
-----------------------------------------------------------------------------

Copyright (c) 2010 Nigel Atkinson

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-----------------------------------------------------------------------------
*/
#include "luaresource.h"

LuaResource::LuaResource(Ogre::ResourceManager* creator, const Ogre::String& name,
    Ogre::ResourceHandle handle, const Ogre::String& group ,
    bool isManual , Ogre::ManualResourceLoader* loader) :
    Ogre::Resource( creator, name, handle, group, isManual, loader )
{
    createParamDictionary("LuaResource");
}

LuaResource::~LuaResource()
{
    unload();
}

size_t LuaResource::calculateSize() const
{
    return mScriptSource.length();
}

void LuaResource::unloadImpl()
{
    mScriptSource.clear();
}

void LuaResource::loadImpl()
{
    Ogre::DataStreamPtr stream = Ogre::ResourceGroupManager::getSingleton()
        .openResource( mName, mGroup, true, this );

    mScriptSource = stream->getAsString();
}

const Ogre::String LuaResource::getScriptSource() const
{
    return mScriptSource;
}

#if OGRE_VERSION < 67584 /* 1.8.0 */
template<>
LuaResourceManager* Ogre::Singleton<LuaResourceManager>::ms_Singleton = 0;
#else
template<>
LuaResourceManager* Ogre::Singleton<LuaResourceManager>::msSingleton = 0;
#endif

LuaResourceManager::LuaResourceManager()
{
    mResourceType = "LuaScript";
    mLoadOrder = 50.0f;

    // Register with OGRE
    Ogre::ResourceGroupManager::getSingleton()._registerResourceManager( mResourceType, this );
}

LuaResourceManager::~LuaResourceManager()
{
    Ogre::ResourceGroupManager::getSingleton()._unregisterResourceManager(mResourceType);
}

Ogre::Resource* LuaResourceManager::createImpl(const Ogre::String& name, Ogre::ResourceHandle handle, const Ogre::String& group, bool isManual, Ogre::ManualResourceLoader* loader, const Ogre::NameValuePairList* createParams)
{
    return new LuaResource( this, name, handle, group, isManual, loader );
}

LuaResourcePtr LuaResourceManager::load(const Ogre::String& name, const Ogre::String& group)
{
    LuaResourcePtr script = getByName(name);

    if( script.isNull() )   // Not yet created
        script = create( name, group );

    script->load();

    return script;
}

#if OGRE_VERSION < 67584 /* 1.8.0 */
LuaResourceManager * LuaResourceManager::getSingeltonPtr()
{
    return ms_Singleton;
}

LuaResourceManager & LuaResourceManager::getSingleton()
{
    assert(ms_Singleton);
    return *ms_Singleton;
}
#else
LuaResourceManager * LuaResourceManager::getSingeltonPtr()
{
    return msSingleton;
}

LuaResourceManager & LuaResourceManager::getSingleton()
{
    assert(msSingleton);
    return *msSingleton;
}
#endif

<|endoftext|>


<commit_before>// Jubatus: Online machine learning framework for distributed environment
// Copyright (C) 2011 Preferred Infrastructure and Nippon Telegraph and Telephone Corporation.
//
// 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

#include "classifier_factory.hpp"

#include <string>

#include "classifier.hpp"
#include "../common/exception.hpp"
#include "../common/jsonconfig.hpp"
#include "../storage/storage_base.hpp"
#include "../unlearner/unlearner_factory.hpp"

using jubatus::core::common::jsonconfig::config;;
using jubatus::core::common::jsonconfig::config_cast_check;
using jubatus::util::lang::shared_ptr;

namespace jubatus {
namespace core {
namespace classifier {
namespace {

struct unlearner_config {
  jubatus::util::data::optional<std::string> unlearner;
  jubatus::util::data::optional<config>
      unlearner_parameter;

  template<typename Ar>
  void serialize(Ar& ar) {
    ar & JUBA_MEMBER(unlearner) & JUBA_MEMBER(unlearner_parameter);
  }
};

}  // namespace

shared_ptr<classifier_base> classifier_factory::create_classifier(
    const std::string& name,
    const common::jsonconfig::config& param,
    jubatus::util::lang::shared_ptr<storage::storage_base> storage) {
  unlearner_config conf;
  try {
    conf = config_cast_check<unlearner_config>(param);
  } catch (const common::jsonconfig::cast_check_error& e) {
    // ignore
  }

  jubatus::util::lang::shared_ptr<unlearner::unlearner_base> unlearner;
  if (conf.unlearner) {
    if (!conf.unlearner_parameter) {
      throw JUBATUS_EXCEPTION(common::exception::runtime_error(
          "Unlearner is set but unlearner_parameter is not found"));
    }
    unlearner = unlearner::create_unlearner(
        *conf.unlearner, common::jsonconfig::config(
            *conf.unlearner_parameter));
  }
  shared_ptr<classifier_base> res;
  if (name == "perceptron") {
    // perceptron doesn't have parameter
    res.reset(new perceptron(storage));
  } else if (name == "PA" || name == "passive_aggressive") {
    // passive_aggressive doesn't have parameter
    res.reset(new passive_aggressive(storage));
  } else if (name == "PA1" || name == "passive_aggressive_1") {
    res.reset(new passive_aggressive_1(
        config_cast_check<classifier_config>(param), storage));
  } else if (name == "PA2" || name == "passive_aggressive_2") {
    res.reset(new passive_aggressive_2(
        config_cast_check<classifier_config>(param), storage));
  } else if (name == "CW" || name == "confidence_weighted") {
    res.reset(new confidence_weighted(
        config_cast_check<classifier_config>(param), storage));
  } else if (name == "AROW" || name == "arow") {
    res.reset(new arow(
        config_cast_check<classifier_config>(param), storage));
  } else if (name == "NHERD" || name == "normal_herd") {
    res.reset(new normal_herd(
        config_cast_check<classifier_config>(param), storage));
  } else {
    throw JUBATUS_EXCEPTION(common::unsupported_method(name));
  }
  res->set_label_unlearner(unlearner);
  return res;
}

}  // namespace classifier
}  // namespace core
}  // namespace jubatus
<commit_msg>remove unneeded deep copy of unlearner_parameter<commit_after>// Jubatus: Online machine learning framework for distributed environment
// Copyright (C) 2011 Preferred Infrastructure and Nippon Telegraph and Telephone Corporation.
//
// 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

#include "classifier_factory.hpp"

#include <string>

#include "classifier.hpp"
#include "../common/exception.hpp"
#include "../common/jsonconfig.hpp"
#include "../storage/storage_base.hpp"
#include "../unlearner/unlearner_factory.hpp"

using jubatus::core::common::jsonconfig::config;;
using jubatus::core::common::jsonconfig::config_cast_check;
using jubatus::util::lang::shared_ptr;

namespace jubatus {
namespace core {
namespace classifier {
namespace {

struct unlearner_config {
  jubatus::util::data::optional<std::string> unlearner;
  jubatus::util::data::optional<config>
      unlearner_parameter;

  template<typename Ar>
  void serialize(Ar& ar) {
    ar & JUBA_MEMBER(unlearner) & JUBA_MEMBER(unlearner_parameter);
  }
};

}  // namespace

shared_ptr<classifier_base> classifier_factory::create_classifier(
    const std::string& name,
    const common::jsonconfig::config& param,
    jubatus::util::lang::shared_ptr<storage::storage_base> storage) {
  unlearner_config conf;
  try {
    conf = config_cast_check<unlearner_config>(param);
  } catch (const common::jsonconfig::cast_check_error& e) {
    // ignore
  }

  jubatus::util::lang::shared_ptr<unlearner::unlearner_base> unlearner;
  if (conf.unlearner) {
    if (!conf.unlearner_parameter) {
      throw JUBATUS_EXCEPTION(common::exception::runtime_error(
          "Unlearner is set but unlearner_parameter is not found"));
    }
    unlearner = unlearner::create_unlearner(
        *conf.unlearner, *conf.unlearner_parameter);
  }
  shared_ptr<classifier_base> res;
  if (name == "perceptron") {
    // perceptron doesn't have parameter
    res.reset(new perceptron(storage));
  } else if (name == "PA" || name == "passive_aggressive") {
    // passive_aggressive doesn't have parameter
    res.reset(new passive_aggressive(storage));
  } else if (name == "PA1" || name == "passive_aggressive_1") {
    res.reset(new passive_aggressive_1(
        config_cast_check<classifier_config>(param), storage));
  } else if (name == "PA2" || name == "passive_aggressive_2") {
    res.reset(new passive_aggressive_2(
        config_cast_check<classifier_config>(param), storage));
  } else if (name == "CW" || name == "confidence_weighted") {
    res.reset(new confidence_weighted(
        config_cast_check<classifier_config>(param), storage));
  } else if (name == "AROW" || name == "arow") {
    res.reset(new arow(
        config_cast_check<classifier_config>(param), storage));
  } else if (name == "NHERD" || name == "normal_herd") {
    res.reset(new normal_herd(
        config_cast_check<classifier_config>(param), storage));
  } else {
    throw JUBATUS_EXCEPTION(common::unsupported_method(name));
  }
  res->set_label_unlearner(unlearner);
  return res;
}

}  // namespace classifier
}  // namespace core
}  // namespace jubatus
<|endoftext|>


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

#include "osl/thread.h"
#include "otherjre.hxx"

using namespace std;

using ::rtl::OUString;
using ::rtl::Reference;
namespace jfw_plugin
{

Reference<VendorBase> OtherInfo::createInstance()
{
    return new OtherInfo;
}

char const* const* OtherInfo::getJavaExePaths(int * size)
{
    static char const * ar[] = {
#if defined(WNT)
        "bin/java.exe",
        "jre/bin/java.exe"
#elif UNX
        "bin/java",
        "jre/bin/java"
#endif
    };
        *size = sizeof (ar) / sizeof (char*);
    return ar;
}

char const* const* OtherInfo::getRuntimePaths(int * size)
{
    static char const* ar[]= {
#if defined(WNT)
        "/bin/client/jvm.dll",
        "/bin/hotspot/jvm.dll",
        "/bin/classic/jvm.dll",
    "/bin/jrockit/jvm.dll"
#elif UNX
#ifdef MACOSX
        "/../../../../../Frameworks/JavaVM.framework/JavaVM"  //as of  1.6.0_22
#else
        "/lib/" JFW_PLUGIN_ARCH "/client/libjvm.so", // for Blackdown PPC
        "/lib/" JFW_PLUGIN_ARCH "/server/libjvm.so", // for Blackdown AMD64
        "/lib/" JFW_PLUGIN_ARCH "/classic/libjvm.so", // for Blackdown PPC
        "/lib/" JFW_PLUGIN_ARCH "/jrockit/libjvm.so", // for Java of BEA Systems
        "/bin/classic/libjvm.so", // fallback for older for IBM Java
        "/jre/bin/classic/libjvm.so" // fallback for older for IBM Java
#endif
#endif

    };
    *size = sizeof(ar) / sizeof (char*);
    return ar;
}

char const* const* OtherInfo::getLibraryPaths(int* size)
{

#ifdef UNX
    static char const * ar[] = {
#ifdef MACOSX
        //mac version does not have a ld library path anymore
#else
        "/bin",
        "/jre/bin",
        "/bin/classic",
        "/jre/bin/classic",
        "/lib/" JFW_PLUGIN_ARCH "/client",
        "/lib/" JFW_PLUGIN_ARCH "/server",
        "/lib/" JFW_PLUGIN_ARCH "/classic",
        "/lib/" JFW_PLUGIN_ARCH "/jrockit",
        "/lib/" JFW_PLUGIN_ARCH "/native_threads",
        "/lib/" JFW_PLUGIN_ARCH
#endif
    };

    *size = sizeof(ar) / sizeof (char*);
    return ar;
#else
    size = 0;
    return NULL;
#endif
}

int OtherInfo::compareVersions(const rtl::OUString& /*sSecond*/) const
{
    //Need to provide an own algorithm for comparing version.
    //Because this function returns always 0, which means the version of
    //this JRE and the provided version "sSecond" are equal, one cannot put
    //any excludeVersion entries in the javavendors.xml file.
    return 0;
}

}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
<commit_msg>Fix nasty ppc ld crash, correct default return param setting<commit_after>/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * Copyright 2000, 2010 Oracle and/or its affiliates.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/

#include "osl/thread.h"
#include "otherjre.hxx"

using namespace std;

using ::rtl::OUString;
using ::rtl::Reference;
namespace jfw_plugin
{

Reference<VendorBase> OtherInfo::createInstance()
{
    return new OtherInfo;
}

char const* const* OtherInfo::getJavaExePaths(int * size)
{
    static char const * ar[] = {
#if defined(WNT)
        "bin/java.exe",
        "jre/bin/java.exe"
#elif UNX
        "bin/java",
        "jre/bin/java"
#endif
    };
        *size = sizeof (ar) / sizeof (char*);
    return ar;
}

char const* const* OtherInfo::getRuntimePaths(int * size)
{
    static char const* ar[]= {
#if defined(WNT)
        "/bin/client/jvm.dll",
        "/bin/hotspot/jvm.dll",
        "/bin/classic/jvm.dll",
    "/bin/jrockit/jvm.dll"
#elif UNX
#ifdef MACOSX
        "/../../../../../Frameworks/JavaVM.framework/JavaVM"  //as of  1.6.0_22
#else
        "/lib/" JFW_PLUGIN_ARCH "/client/libjvm.so", // for Blackdown PPC
        "/lib/" JFW_PLUGIN_ARCH "/server/libjvm.so", // for Blackdown AMD64
        "/lib/" JFW_PLUGIN_ARCH "/classic/libjvm.so", // for Blackdown PPC
        "/lib/" JFW_PLUGIN_ARCH "/jrockit/libjvm.so", // for Java of BEA Systems
        "/bin/classic/libjvm.so", // fallback for older for IBM Java
        "/jre/bin/classic/libjvm.so" // fallback for older for IBM Java
#endif
#endif

    };
    *size = sizeof(ar) / sizeof (char*);
    return ar;
}

char const* const* OtherInfo::getLibraryPaths(int* size)
{

#if defined(UNX) && !defined(MACOSX)
    //mac version does not have a ld library path anymore
    static char const * ar[] = {
        "/bin",
        "/jre/bin",
        "/bin/classic",
        "/jre/bin/classic",
        "/lib/" JFW_PLUGIN_ARCH "/client",
        "/lib/" JFW_PLUGIN_ARCH "/server",
        "/lib/" JFW_PLUGIN_ARCH "/classic",
        "/lib/" JFW_PLUGIN_ARCH "/jrockit",
        "/lib/" JFW_PLUGIN_ARCH "/native_threads",
        "/lib/" JFW_PLUGIN_ARCH
    };

    *size = sizeof(ar) / sizeof (char*);
    return ar;
#else
    *size = 0;
    return NULL;
#endif
}

int OtherInfo::compareVersions(const rtl::OUString& /*sSecond*/) const
{
    //Need to provide an own algorithm for comparing version.
    //Because this function returns always 0, which means the version of
    //this JRE and the provided version "sSecond" are equal, one cannot put
    //any excludeVersion entries in the javavendors.xml file.
    return 0;
}

}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
<|endoftext|>


<commit_before>/** This file is part of Contacts daemon
 **
 ** Copyright (c) 2010-2011 Nokia Corporation and/or its subsidiary(-ies).
 **
 ** Contact:  Nokia Corporation (info@qt.nokia.com)
 **
 ** GNU Lesser General Public License Usage
 ** This file may be used under the terms of the GNU Lesser General Public License
 ** version 2.1 as published by the Free Software Foundation and appearing in the
 ** file LICENSE.LGPL included in the packaging of this file.  Please review the
 ** following information to ensure the GNU Lesser General Public License version
 ** 2.1 requirements will be met:
 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
 **
 ** In addition, as a special exception, Nokia gives you certain additional rights.
 ** These rights are described in the Nokia Qt LGPL Exception version 1.1, included
 ** in the file LGPL_EXCEPTION.txt in this package.
 **
 ** Other Usage
 ** Alternatively, this file may be used in accordance with the terms and
 ** conditions contained in a signed written agreement between you and Nokia.
 **/

#include "gcplugin.h"
#include "garbagecollectoradaptor.h"

#include "debug.h"

// For fsync (impossible to do with Qt directly)
#include <unistd.h>
#include <stdio.h>
#include <errno.h>

using namespace Contactsd;

const QLatin1String DBusObjectPath("/com/nokia/contacts/GarbageCollector1");

GcPlugin::GcPlugin()
{
    if (registerDBusObject()) {
        (void) new GarbageCollectorAdaptor(this);
    }

    loadSavedQueries();
}

GcPlugin::~GcPlugin()
{
    QDBusConnection::sessionBus().unregisterObject(DBusObjectPath);
}

void GcPlugin::init()
{
    debug() << "Initializing contactsd GarbageCollector plugin";
}

bool GcPlugin::registerDBusObject()
{
    QDBusConnection connection = QDBusConnection::sessionBus();
    if (!connection.isConnected()) {
        warning() << "Could not connect to DBus:" << connection.lastError();
        return false;
    }

    if (!connection.registerObject(DBusObjectPath, this)) {
        warning() << "Could not register DBus object '/':" <<
            connection.lastError();
        return false;
    }
    return true;
}

void GcPlugin::loadSavedQueries()
{
    const QHash<QString, QueryStorage::Query> queries = mStorage.queries();

    for(QHash<QString, QueryStorage::Query>::const_iterator it = queries.constBegin();
        it != queries.constEnd(); ++it) {
        debug() << "Loading saved query" << it.key() << it.value().first<< it.value().second;
        Collector *c = new Collector(it.key(), it.value().first, this);
        c->setLoad(it.value().second);
        mCollectors[it.key()] = c;
    }
}

void GcPlugin::Register(const QString &id, const QString &query)
{
    mStorage.addQuery(id, query);

    if (mCollectors.contains(id)) {
        mCollectors[id]->setQuery(query);
        return;
    }

    mCollectors[id] = new Collector(id, query, this);
    debug() << "New GarbageCollector query registered for id" << id;
}

void GcPlugin::Unregister(const QString &id)
{
    if (!mCollectors.contains(id)) {
        debug() << "Error: unknown collector id" << id;
        return;
    }

    debug() << "Unregistering query with id" << id;

    mStorage.removeQuery(id);
    delete mCollectors.take(id);
}

void GcPlugin::Trigger(const QString &id, double load_increment)
{
    if (!mCollectors.contains(id)) {
        debug() << "Error: unknown collector id" << id;
        return;
    }

    Collector *c = mCollectors[id];
    c->trigger(load_increment);

    debug() << "increased query" << id << "by" << load_increment << "total load:" << c->load();

    mStorage.updateLoad(id, c->load());
}

GcPlugin::MetaData GcPlugin::metaData()
{
    MetaData data;
    data[metaDataKeyName]    = QVariant(QString::fromLatin1("garbage-collector"));
    data[metaDataKeyVersion] = QVariant(QString::fromLatin1("0.1"));
    data[metaDataKeyComment] = QVariant(QString::fromLatin1("contactsd garbage collector plugin"));
    return data;
}

///////////////////////////////////////////////////////////////////////////////

static const int TriggerTimeout = 10; // seconds

Collector::Collector(const QString &id, const QString &q, QObject *parent)
    : QObject(parent)
    , mId(id)
    , mQuery(q)
    , mLoad(0)
{
    mTimer.setInterval(TriggerTimeout*1000);
    mTimer.setSingleShot(true);
    connect(&mTimer, SIGNAL(timeout()), SLOT(onTimeout()));
}

void Collector::trigger(double v)
{
    mLoad += v;
    if (mLoad >= 1) {
        // Restarts the timer if already running
        mTimer.start();
    }
}

void Collector::onTimeout()
{
    debug() << "Launch query for collector" << mId;
    mLoad = 0;

    QSparqlConnection &connection = BasePlugin::sparqlConnection();
    static QSparqlQueryOptions queryOptions;
    queryOptions.setPriority(QSparqlQueryOptions::LowPriority);

    QSparqlResult *result = connection.exec(QSparqlQuery(mQuery, QSparqlQuery::DeleteStatement),
                                            queryOptions);

    if (not result) {
        warning() << "QSparqlConnection::exec() == 0";
        return;
    }
    if (result->hasError()) {
        warning() << "Error exec query:" << result->lastError().message();
        delete result;
        return;
    }

    result->setParent(this);
    connect(result, SIGNAL(finished()), SLOT(onQueryFinished()), Qt::QueuedConnection);
}

void Collector::onQueryFinished()
{
    QSparqlResult *result = qobject_cast<QSparqlResult *>(sender());

    if (not result) {
        warning() << "QSparqlQuery finished with error:" << "Invalid signal sender";
    } else {
        if (result->hasError()) {
            warning() << "QSparqlQuery finished with error:" << result->lastError();
        }
        result->deleteLater();
    }
}

///////////////////////////////////////////////////////////////////////////////

QueryStorage::QueryStorage()
{
    // We can't use a simple QSettings, because it's not crash-safe (ie can get
    // corrupted if device crashes while saving). So we just use two simple
    // custom function (un)serializing the QHash to a file.
    load();
}

QueryStorage::~QueryStorage()
{
}

QHash<QString, QueryStorage::Query>
QueryStorage::queries() const
{
    return mQueries;
}

void QueryStorage::setQueries(const QHash<QString, Query> &queries)
{
    mQueries = queries;
    save();
}

void QueryStorage::addQuery(const QString &id, const QString &query)
{
    Query q = qMakePair(query, 0.);
    mQueries.insert(id, q);
    save();
}

void QueryStorage::updateLoad(const QString &id, double value)
{
    QHash<QString, Query>::iterator it = mQueries.find(id);

    if (it == mQueries.constEnd()) {
        return;
    }

    Query q = *it;
    q.second = value;

    mQueries.insert(id, q);

    save();
}

void QueryStorage::removeQuery(const QString &id)
{
    if (mQueries.remove(id)) {
        save();
    }
}

QString QueryStorage::filePath()
{
    static const QString cacheFileName = "queries";
    static QString path;

    if (not path.isNull()) {
        return path;
    }

    QDir cacheDir;
    const char *xdgCacheDir = getenv("XDG_CACHE_DIR");

    if (xdgCacheDir != 0) {
        cacheDir = QDir(QString::fromLocal8Bit(xdgCacheDir).append("/contactsd"));
    } else {
        cacheDir = QDir(QDir::home().absoluteFilePath(QString::fromLatin1(".cache/contactsd")));
    }

    if (not cacheDir.exists()) {
        if (not QDir::root().mkpath(cacheDir.absolutePath())) {
            warning() << "Could not create cache dir";
            return QString();
        }
    }

    path = cacheDir.absoluteFilePath(cacheFileName);
    return path;
}

void QueryStorage::load()
{
    QFile file(filePath());

    if (not file.exists()) {
        return;
    }

    if (not file.open(QIODevice::ReadOnly)) {
        warning() << "Could not open queries file " << file.fileName() << ":" << file.errorString();
        return;
    }

    QDataStream stream(&file);
    stream >> mQueries;

    file.close();
}

void QueryStorage::save()
{
    QTemporaryFile tmpFile(filePath());
    tmpFile.setAutoRemove(false);

    if (not tmpFile.open()) {
        warning() << "Could not create temporary file:" << tmpFile.errorString();
        return;
    }

    QDataStream stream(&tmpFile);
    stream << mQueries;

    if (not tmpFile.flush()) {
        warning() << "Could not write queries to disk:" << tmpFile.errorString();
        return;
    }

    int errorCode = -1;

    if ((errorCode = fsync(tmpFile.handle())) != 0) {
        warning() << "Could not sync queries to disk:" << strerror(errorCode);
        return;
    }

    tmpFile.close();

    // We can't use Qt's rename because it won't overwrite an existing file
    errorCode = -1;
    if (rename(tmpFile.fileName().toLocal8Bit(), filePath().toLocal8Bit()) != 0) {
        warning() << "Could not overwrite old queries file:" << strerror(errorCode);
        return;
    }
}

///////////////////////////////////////////////////////////////////////////////

Q_EXPORT_PLUGIN2(garbagecollectorplugin, GcPlugin)
<commit_msg>Changes: garbage-collector: Fix error reporting for POSIX calls<commit_after>/** This file is part of Contacts daemon
 **
 ** Copyright (c) 2010-2011 Nokia Corporation and/or its subsidiary(-ies).
 **
 ** Contact:  Nokia Corporation (info@qt.nokia.com)
 **
 ** GNU Lesser General Public License Usage
 ** This file may be used under the terms of the GNU Lesser General Public License
 ** version 2.1 as published by the Free Software Foundation and appearing in the
 ** file LICENSE.LGPL included in the packaging of this file.  Please review the
 ** following information to ensure the GNU Lesser General Public License version
 ** 2.1 requirements will be met:
 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
 **
 ** In addition, as a special exception, Nokia gives you certain additional rights.
 ** These rights are described in the Nokia Qt LGPL Exception version 1.1, included
 ** in the file LGPL_EXCEPTION.txt in this package.
 **
 ** Other Usage
 ** Alternatively, this file may be used in accordance with the terms and
 ** conditions contained in a signed written agreement between you and Nokia.
 **/

#include "gcplugin.h"
#include "garbagecollectoradaptor.h"

#include "debug.h"

// For fsync (impossible to do with Qt directly)
#include <unistd.h>
#include <stdio.h>
#include <errno.h>

using namespace Contactsd;

const QLatin1String DBusObjectPath("/com/nokia/contacts/GarbageCollector1");

GcPlugin::GcPlugin()
{
    if (registerDBusObject()) {
        (void) new GarbageCollectorAdaptor(this);
    }

    loadSavedQueries();
}

GcPlugin::~GcPlugin()
{
    QDBusConnection::sessionBus().unregisterObject(DBusObjectPath);
}

void GcPlugin::init()
{
    debug() << "Initializing contactsd GarbageCollector plugin";
}

bool GcPlugin::registerDBusObject()
{
    QDBusConnection connection = QDBusConnection::sessionBus();
    if (!connection.isConnected()) {
        warning() << "Could not connect to DBus:" << connection.lastError();
        return false;
    }

    if (!connection.registerObject(DBusObjectPath, this)) {
        warning() << "Could not register DBus object '/':" <<
            connection.lastError();
        return false;
    }
    return true;
}

void GcPlugin::loadSavedQueries()
{
    const QHash<QString, QueryStorage::Query> queries = mStorage.queries();

    for(QHash<QString, QueryStorage::Query>::const_iterator it = queries.constBegin();
        it != queries.constEnd(); ++it) {
        debug() << "Loading saved query" << it.key() << it.value().first<< it.value().second;
        Collector *c = new Collector(it.key(), it.value().first, this);
        c->setLoad(it.value().second);
        mCollectors[it.key()] = c;
    }
}

void GcPlugin::Register(const QString &id, const QString &query)
{
    mStorage.addQuery(id, query);

    if (mCollectors.contains(id)) {
        mCollectors[id]->setQuery(query);
        return;
    }

    mCollectors[id] = new Collector(id, query, this);
    debug() << "New GarbageCollector query registered for id" << id;
}

void GcPlugin::Unregister(const QString &id)
{
    if (!mCollectors.contains(id)) {
        debug() << "Error: unknown collector id" << id;
        return;
    }

    debug() << "Unregistering query with id" << id;

    mStorage.removeQuery(id);
    delete mCollectors.take(id);
}

void GcPlugin::Trigger(const QString &id, double load_increment)
{
    if (!mCollectors.contains(id)) {
        debug() << "Error: unknown collector id" << id;
        return;
    }

    Collector *c = mCollectors[id];
    c->trigger(load_increment);

    debug() << "increased query" << id << "by" << load_increment << "total load:" << c->load();

    mStorage.updateLoad(id, c->load());
}

GcPlugin::MetaData GcPlugin::metaData()
{
    MetaData data;
    data[metaDataKeyName]    = QVariant(QString::fromLatin1("garbage-collector"));
    data[metaDataKeyVersion] = QVariant(QString::fromLatin1("0.1"));
    data[metaDataKeyComment] = QVariant(QString::fromLatin1("contactsd garbage collector plugin"));
    return data;
}

///////////////////////////////////////////////////////////////////////////////

static const int TriggerTimeout = 10; // seconds

Collector::Collector(const QString &id, const QString &q, QObject *parent)
    : QObject(parent)
    , mId(id)
    , mQuery(q)
    , mLoad(0)
{
    mTimer.setInterval(TriggerTimeout*1000);
    mTimer.setSingleShot(true);
    connect(&mTimer, SIGNAL(timeout()), SLOT(onTimeout()));
}

void Collector::trigger(double v)
{
    mLoad += v;
    if (mLoad >= 1) {
        // Restarts the timer if already running
        mTimer.start();
    }
}

void Collector::onTimeout()
{
    debug() << "Launch query for collector" << mId;
    mLoad = 0;

    QSparqlConnection &connection = BasePlugin::sparqlConnection();
    static QSparqlQueryOptions queryOptions;
    queryOptions.setPriority(QSparqlQueryOptions::LowPriority);

    QSparqlResult *result = connection.exec(QSparqlQuery(mQuery, QSparqlQuery::DeleteStatement),
                                            queryOptions);

    if (not result) {
        warning() << "QSparqlConnection::exec() == 0";
        return;
    }
    if (result->hasError()) {
        warning() << "Error exec query:" << result->lastError().message();
        delete result;
        return;
    }

    result->setParent(this);
    connect(result, SIGNAL(finished()), SLOT(onQueryFinished()), Qt::QueuedConnection);
}

void Collector::onQueryFinished()
{
    QSparqlResult *result = qobject_cast<QSparqlResult *>(sender());

    if (not result) {
        warning() << "QSparqlQuery finished with error:" << "Invalid signal sender";
    } else {
        if (result->hasError()) {
            warning() << "QSparqlQuery finished with error:" << result->lastError();
        }
        result->deleteLater();
    }
}

///////////////////////////////////////////////////////////////////////////////

QueryStorage::QueryStorage()
{
    // We can't use a simple QSettings, because it's not crash-safe (ie can get
    // corrupted if device crashes while saving). So we just use two simple
    // custom function (un)serializing the QHash to a file.
    load();
}

QueryStorage::~QueryStorage()
{
}

QHash<QString, QueryStorage::Query>
QueryStorage::queries() const
{
    return mQueries;
}

void QueryStorage::setQueries(const QHash<QString, Query> &queries)
{
    mQueries = queries;
    save();
}

void QueryStorage::addQuery(const QString &id, const QString &query)
{
    Query q = qMakePair(query, 0.);
    mQueries.insert(id, q);
    save();
}

void QueryStorage::updateLoad(const QString &id, double value)
{
    QHash<QString, Query>::iterator it = mQueries.find(id);

    if (it == mQueries.constEnd()) {
        return;
    }

    Query q = *it;
    q.second = value;

    mQueries.insert(id, q);

    save();
}

void QueryStorage::removeQuery(const QString &id)
{
    if (mQueries.remove(id)) {
        save();
    }
}

QString QueryStorage::filePath()
{
    static const QString cacheFileName = "queries";
    static QString path;

    if (not path.isNull()) {
        return path;
    }

    QDir cacheDir;
    const char *xdgCacheDir = getenv("XDG_CACHE_DIR");

    if (xdgCacheDir != 0) {
        cacheDir = QDir(QString::fromLocal8Bit(xdgCacheDir).append("/contactsd"));
    } else {
        cacheDir = QDir(QDir::home().absoluteFilePath(QString::fromLatin1(".cache/contactsd")));
    }

    if (not cacheDir.exists()) {
        if (not QDir::root().mkpath(cacheDir.absolutePath())) {
            warning() << "Could not create cache dir";
            return QString();
        }
    }

    path = cacheDir.absoluteFilePath(cacheFileName);
    return path;
}

void QueryStorage::load()
{
    QFile file(filePath());

    if (not file.exists()) {
        return;
    }

    if (not file.open(QIODevice::ReadOnly)) {
        warning() << "Could not open queries file " << file.fileName() << ":" << file.errorString();
        return;
    }

    QDataStream stream(&file);
    stream >> mQueries;

    file.close();
}

void QueryStorage::save()
{
    QTemporaryFile tmpFile(filePath());
    tmpFile.setAutoRemove(false);

    if (not tmpFile.open()) {
        warning() << "Could not create temporary file:" << tmpFile.errorString();
        return;
    }

    QDataStream stream(&tmpFile);
    stream << mQueries;

    if (not tmpFile.flush()) {
        warning() << "Could not write queries to disk:" << tmpFile.errorString();
        return;
    }

    if (fsync(tmpFile.handle()) != 0) {
        warning() << "Could not sync queries to disk:" << strerror(errno);
        return;
    }

    tmpFile.close();

    // We can't use Qt's rename because it won't overwrite an existing file
    if (rename(tmpFile.fileName().toLocal8Bit(), filePath().toLocal8Bit()) != 0) {
        warning() << "Could not overwrite old queries file:" << strerror(errno);
        return;
    }
}

///////////////////////////////////////////////////////////////////////////////

Q_EXPORT_PLUGIN2(garbagecollectorplugin, GcPlugin)
<|endoftext|>