<commit_before>
/// This file is part of the KDE libraries
/// Copyright (C) 2005 Christoph Cullmann <cullmann@kde.org>
/// Copyright (C) 2005 Joseph Wenninger <jowenn@kde.org>
/// Copyright (C) 2006 Dominik Haumann <dhaumann kde org>
/// Copyright (C) 2008 Paul Giannaros <paul@giannaros.org>
///
/// This library is free software; you can redistribute it and/or
/// modify it under the terms of the GNU Library General Public
/// License version 2 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
/// Library General Public License for more details.
///
/// You should have received a copy of the GNU Library General Public License
/// along with this library; see the file COPYING.LIB.  If not, write to
/// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
/// Boston, MA 02110-1301, USA.

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <iostream>

#include <QFile>
#include <QFileInfo>
#include <QStringList>
#include <QMap>

#include <kconfig.h>
#include <kconfiggroup.h>
#include <kstandarddirs.h>

#include "kateglobal.h"

#include "katescriptmanager.h"

KateScriptManager::KateScriptManager() : KTextEditor::Command()
{
  // false = force (ignore cache)
  collect("katepartscriptrc", "katepart/script/*.js", false);
}

KateScriptManager::~KateScriptManager()
{
  qDeleteAll(m_scripts);
}

KateIndentScript *KateScriptManager::indenter(const QString &language)
{
  KateIndentScript *highestPriorityIndenter = 0;
  foreach(KateIndentScript *indenter, m_languageToIndenters.value(language.toLower())) {
    // don't overwrite if there is already a result with a higher priority
    if(highestPriorityIndenter && indenter->information().priority < highestPriorityIndenter->information().priority) {
      kDebug(13050) << "KateScriptManager::indenter: Not overwriting indenter for '"
                    << language << "' as the priority isn't big enough (" <<
                    indenter->information().priority << " < "
                    << highestPriorityIndenter->information().priority << ')';
    }
    else {
      highestPriorityIndenter = indenter;
    }
  }
  if(!highestPriorityIndenter) {
    kDebug(13050) << "KateScriptManager::indenter: No indenter for " << language;
  }
  return highestPriorityIndenter;
}

void KateScriptManager::collect(const QString& resourceFile,
                                const QString& directory,
                                bool force)
{
  KConfig cfgFile(resourceFile, KConfig::NoGlobals);
  KConfigGroup config = cfgFile.group("General");

  force = false;
  // If KatePart version does not match, better force a true reload
  if(QString(KATEPART_VERSION) != config.readEntry("kate-version", QString("0.0"))) {
    config.writeEntry("kate-version", QString(KATEPART_VERSION));
    force = true;
  }
  // get a list of all .js files
  QStringList list = KGlobal::dirs()->findAllResources("data", directory, KStandardDirs::NoDuplicates);
  // clear out the old scripts and reserve enough space
  qDeleteAll(m_scripts);
  m_scripts.clear();
  m_languageToIndenters.clear();
  m_scripts.reserve(list.size());

  // iterate through the files and read info out of cache or file
  for(QStringList::ConstIterator fileit = list.begin(); fileit != list.end(); ++fileit) {
    // get abs filename....
    QFileInfo fi(*fileit);
    QString absPath = fi.absoluteFilePath();
    QString baseName = fi.baseName ();

    // each file has a group
    QString group = "Cache "+ *fileit;
    config.changeGroup(group);

    // stat the file to get the last-modified-time
    struct stat sbuf;
    memset(&sbuf, 0, sizeof(sbuf));
    stat(QFile::encodeName(*fileit), &sbuf);

    // check whether file is already cached
    bool useCache = false;
    if(!force && cfgFile.hasGroup(group)) {
      useCache = (sbuf.st_mtime == config.readEntry("last-modified", 0));
    }

    // read key/value pairs from the cached file if possible
    // otherwise, parse it and then save the needed infos to the cache.
    QHash<QString, QString> pairs;
    if(useCache) {
      QMap<QString, QString> entries = config.entryMap();
      for(QMap<QString, QString>::ConstIterator entry = entries.begin();
          entry != entries.end();
          ++entry)
        pairs[entry.key()] = entry.value();
    }
    else if(parseMetaInformation(*fileit, pairs)) {
      config.changeGroup(group);
      config.writeEntry("last-modified", int(sbuf.st_mtime));
      // iterate keys and save cache
      for(QHash<QString, QString>::ConstIterator item = pairs.begin();
          item != pairs.end();
          ++item)
        config.writeEntry(item.key(), item.value());
    }
    else {
      // parseMetaInformation will have informed the user of the problem
      continue;
    }
    // make sure we have the necessary meta data items we need for proper execution
    KateScriptInformation information;
    information.baseName = baseName;
    information.name = pairs.take("name");
    if(information.name.isNull()) {
      std::cerr << "Script value error: No name specified in script meta data: "
                << qPrintable(*fileit) << '\n';
      continue;
    }
    information.license = pairs.take("license");
    information.author = pairs.take("author");
    information.version = pairs.take("version");
    information.kateVersion = pairs.take("kate-version");
    QString type = pairs.take("type");
    if(type == "indentation") {
      information.type = Kate::IndentationScript;
    }
    else {
      information.type = Kate::UnknownScript;
    }
    // everything else we don't know about explicitly. It goes into other
    information.other = pairs;
    // now, cast accordingly based on type
    switch(information.type) {
      case Kate::IndentationScript: {
        // which languages does this support?
        QString indentLanguages = pairs.take("indent-languages");
        if(!indentLanguages.isNull()) {
          information.indentLanguages = indentLanguages.split(",");
        }
        else {
          information.indentLanguages = QStringList() << information.name;
          std::cerr << "Script value warning: No indent-languages specified for indent "
                    << "script " << qPrintable(*fileit) << ". Using the name ("
                    << qPrintable(information.name) << ")\n";
        }
        // priority?
        bool convertedToInt;
        int priority = pairs.take("priority").toInt(&convertedToInt);
        if(!convertedToInt) {
          std::cerr << "Script value warning: Unexpected or no priority value "
                    << "in: " << qPrintable(*fileit) << ". Setting priority to 0\n";
        }
        information.priority = convertedToInt ? priority : 0;
        KateIndentScript *script = new KateIndentScript(*fileit, information);
        foreach(QString language, information.indentLanguages) {
          m_languageToIndenters[language.toLower()].push_back(script);
        }
        m_scripts.push_back(script);

 m_indentationScripts.insert (information.baseName, script);
    m_indentationScriptsList.append(script);
        break;
      }
      case Kate::UnknownScript:
      default:
        std::cerr << "Script value warning: Unknown type ('" << qPrintable(type) << "'): "
                  << qPrintable(*fileit) << '\n';
        m_scripts.push_back(new KateScript(*fileit, information));
    }
  }

 // XX Test
  if(indenter("Python")) {
    std::cout << "Python: " << indenter("Python")->global("triggerCharacters").isValid() << "\n";
    std::cout << "Python: " << indenter("Python")->function("triggerCharacters").isValid() << "\n";
    std::cout << "Python: " << indenter("Python")->global("blafldsjfklas").isValid() << "\n";
    std::cout << "Python: " << indenter("Python")->function("indent").isValid() << "\n";
  }
  if(indenter("C"))
    std::cout << "C: " << qPrintable(indenter("C")->url()) << "\n";
  if(indenter("lisp"))
    std::cout << "LISP: " << qPrintable(indenter("Lisp")->url()) << "\n";
  config.sync();
}

bool KateScriptManager::parseMetaInformation(const QString& url,
                                             QHash<QString, QString> &pairs)
{
  // a valid script file -must- have the following format:
  // The first line must contain the string 'kate-script'.
  // All following lines have to have the format 'key : value'. So the value
  // is separated by a colon. Leading non-letter characters are ignored, that
  // include C and C++ comments for example.
  // Parsing the header stops at the first line with no ':'.

  QFile file(QFile::encodeName(url));
  if(!file.open(QIODevice::ReadOnly)) {
    std::cerr << "Script parse error: Cannot open file " << qPrintable(url) << '\n';
    return false;
  }

  kDebug(13050) << "Update script: " << url;
  QTextStream ts(&file);
  ts.setCodec("UTF-8");
  if(!ts.readLine().contains("kate-script")) {
    std::cerr << "Script parse error: No header found in " << qPrintable(url) << '\n';
    file.close();
    return false;
  }

  QString line;
  while(!(line = ts.readLine()).isNull()) {
    int colon = line.indexOf(':');
    if(colon <= 0)
      break; // no colon -> end of header found

    // if -1 then 0. if >= 0, move after star.
    int start = 0; // start points to first letter. idea: skip '*' and '//'
    while(start < line.length() && !line.at(start).isLetter())
      ++start;

    QString key = line.mid(start, colon - start).trimmed();
    QString value = line.right(line.length() - (colon + 1)).trimmed();
    pairs[key] = value;

    kDebug(13050) << "KateScriptManager::parseMetaInformation: found pair: "
                  << "(" << key << " | " << value << ")";
  }
  file.close();
  return true;
}

/// Kate::Command stuff

bool KateScriptManager::exec(KTextEditor::View *view, const QString &_cmd, QString &errorMsg)
{
  QStringList args(_cmd.split(QRegExp("\\s+"), QString::SkipEmptyParts));
  QString cmd(args.first());
  args.removeFirst();
#if 0
  if(!view) {
    errorMsg = i18n("Could not access view");
    return false;
  }

  KateView* kateView = qobject_cast<KateView*>(view);

  if(cmd == QLatin1String("js-run-myself"))
  {
    KateJSInterpreterContext script("");
    return script.evalSource(kateView, kateView->doc()->text(), errorMsg);
  }

  KateJScriptManager::Script *script = m_function2Script.value(cmd);

  if(!script) {
    errorMsg = i18n("Command not found: %1", cmd);
    return false;
  }

  KateJSInterpreterContext *inter = interpreter(script->basename);

  if(!inter)
  {
    errorMsg = i18n("Failed to start interpreter for script %1, command %2", script->basename, cmd);
    return false;
  }

  KJS::List params;

  foreach(const QString &a, args)
    params.append(KJS::jsString(a));

  KJS::JSValue *val = inter->callFunction(kateView, inter->interpreter()->globalObject(), KJS::Identifier(cmd),
                                   params, errorMsg);
#else
  if(!view) {
    errorMsg = i18n("Could not access view");
    return false;
  }
  errorMsg = i18n("Command not found: %1", cmd);
  return false;
#endif
}

bool KateScriptManager::help(KTextEditor::View *, const QString &cmd, QString &msg)
{
#if 0
  if (cmd == "js-run-myself") {
    msg = i18n("This executes the current document as a javascript within kate");
    return true;
  }

  if (!m_scripts.contains(cmd))
    return false;

  msg = m_scripts[cmd]->help;

  return !msg.isEmpty();
#endif
  return true;
}

const QStringList &KateScriptManager::cmds()
{
  static QStringList l;
#if 0
  l.clear();
  l << "js-run-myself";

  QHashIterator<QString, KateJScriptManager::Script*> i(m_function2Script);
  while (i.hasNext()) {
      i.next();
      l << i.key();
  }

#endif
  return l;
}

// kate: space-indent on; indent-width 2; replace-tabs on;
<commit_msg>Typo fixes<commit_after>
/// This file is part of the KDE libraries
/// Copyright (C) 2005 Christoph Cullmann <cullmann@kde.org>
/// Copyright (C) 2005 Joseph Wenninger <jowenn@kde.org>
/// Copyright (C) 2006 Dominik Haumann <dhaumann kde org>
/// Copyright (C) 2008 Paul Giannaros <paul@giannaros.org>
///
/// This library is free software; you can redistribute it and/or
/// modify it under the terms of the GNU Library General Public
/// License version 2 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
/// Library General Public License for more details.
///
/// You should have received a copy of the GNU Library General Public License
/// along with this library; see the file COPYING.LIB.  If not, write to
/// the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
/// Boston, MA 02110-1301, USA.

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <iostream>

#include <QFile>
#include <QFileInfo>
#include <QStringList>
#include <QMap>

#include <kconfig.h>
#include <kconfiggroup.h>
#include <kstandarddirs.h>

#include "kateglobal.h"

#include "katescriptmanager.h"

KateScriptManager::KateScriptManager() : KTextEditor::Command()
{
  // false = force (ignore cache)
  collect("katepartscriptrc", "katepart/script/*.js", false);
}

KateScriptManager::~KateScriptManager()
{
  qDeleteAll(m_scripts);
}

KateIndentScript *KateScriptManager::indenter(const QString &language)
{
  KateIndentScript *highestPriorityIndenter = 0;
  foreach(KateIndentScript *indenter, m_languageToIndenters.value(language.toLower())) {
    // don't overwrite if there is already a result with a higher priority
    if(highestPriorityIndenter && indenter->information().priority < highestPriorityIndenter->information().priority) {
      kDebug(13050) << "KateScriptManager::indenter: Not overwriting indenter for '"
                    << language << "' as the priority isn't big enough (" <<
                    indenter->information().priority << " < "
                    << highestPriorityIndenter->information().priority << ')';
    }
    else {
      highestPriorityIndenter = indenter;
    }
  }
  if(!highestPriorityIndenter) {
    kDebug(13050) << "KateScriptManager::indenter: No indenter for " << language;
  }
  return highestPriorityIndenter;
}

void KateScriptManager::collect(const QString& resourceFile,
                                const QString& directory,
                                bool force)
{
  KConfig cfgFile(resourceFile, KConfig::NoGlobals);
  KConfigGroup config = cfgFile.group("General");

  force = false;
  // If KatePart version does not match, better force a true reload
  if(QString(KATEPART_VERSION) != config.readEntry("kate-version", QString("0.0"))) {
    config.writeEntry("kate-version", QString(KATEPART_VERSION));
    force = true;
  }
  // get a list of all .js files
  QStringList list = KGlobal::dirs()->findAllResources("data", directory, KStandardDirs::NoDuplicates);
  // clear out the old scripts and reserve enough space
  qDeleteAll(m_scripts);
  m_scripts.clear();
  m_languageToIndenters.clear();
  m_scripts.reserve(list.size());

  // iterate through the files and read info out of cache or file
  for(QStringList::ConstIterator fileit = list.begin(); fileit != list.end(); ++fileit) {
    // get abs filename....
    QFileInfo fi(*fileit);
    QString absPath = fi.absoluteFilePath();
    QString baseName = fi.baseName ();

    // each file has a group
    QString group = "Cache "+ *fileit;
    config.changeGroup(group);

    // stat the file to get the last-modified-time
    struct stat sbuf;
    memset(&sbuf, 0, sizeof(sbuf));
    stat(QFile::encodeName(*fileit), &sbuf);

    // check whether file is already cached
    bool useCache = false;
    if(!force && cfgFile.hasGroup(group)) {
      useCache = (sbuf.st_mtime == config.readEntry("last-modified", 0));
    }

    // read key/value pairs from the cached file if possible
    // otherwise, parse it and then save the needed infos to the cache.
    QHash<QString, QString> pairs;
    if(useCache) {
      QMap<QString, QString> entries = config.entryMap();
      for(QMap<QString, QString>::ConstIterator entry = entries.begin();
          entry != entries.end();
          ++entry)
        pairs[entry.key()] = entry.value();
    }
    else if(parseMetaInformation(*fileit, pairs)) {
      config.changeGroup(group);
      config.writeEntry("last-modified", int(sbuf.st_mtime));
      // iterate keys and save cache
      for(QHash<QString, QString>::ConstIterator item = pairs.begin();
          item != pairs.end();
          ++item)
        config.writeEntry(item.key(), item.value());
    }
    else {
      // parseMetaInformation will have informed the user of the problem
      continue;
    }
    // make sure we have the necessary meta data items we need for proper execution
    KateScriptInformation information;
    information.baseName = baseName;
    information.name = pairs.take("name");
    if(information.name.isNull()) {
      std::cerr << "Script value error: No name specified in script meta data: "
                << qPrintable(*fileit) << '\n';
      continue;
    }
    information.license = pairs.take("license");
    information.author = pairs.take("author");
    information.version = pairs.take("version");
    information.kateVersion = pairs.take("kate-version");
    QString type = pairs.take("type");
    if(type == "indentation") {
      information.type = Kate::IndentationScript;
    }
    else {
      information.type = Kate::UnknownScript;
    }
    // everything else we don't know about explicitly. It goes into other
    information.other = pairs;
    // now, cast accordingly based on type
    switch(information.type) {
      case Kate::IndentationScript: {
        // which languages does this support?
        QString indentLanguages = pairs.take("indent-languages");
        if(!indentLanguages.isNull()) {
          information.indentLanguages = indentLanguages.split(",");
        }
        else {
          information.indentLanguages = QStringList() << information.name;
          std::cerr << "Script value warning: No indent-languages specified for indent "
                    << "script " << qPrintable(*fileit) << ". Using the name ("
                    << qPrintable(information.name) << ")\n";
        }
        // priority?
        bool convertedToInt;
        int priority = pairs.take("priority").toInt(&convertedToInt);
        if(!convertedToInt) {
          std::cerr << "Script value warning: Unexpected or no priority value "
                    << "in: " << qPrintable(*fileit) << ". Setting priority to 0\n";
        }
        information.priority = convertedToInt ? priority : 0;
        KateIndentScript *script = new KateIndentScript(*fileit, information);
        foreach(QString language, information.indentLanguages) {
          m_languageToIndenters[language.toLower()].push_back(script);
        }
        m_scripts.push_back(script);

 m_indentationScripts.insert (information.baseName, script);
    m_indentationScriptsList.append(script);
        break;
      }
      case Kate::UnknownScript:
      default:
        std::cerr << "Script value warning: Unknown type ('" << qPrintable(type) << "'): "
                  << qPrintable(*fileit) << '\n';
        m_scripts.push_back(new KateScript(*fileit, information));
    }
  }

 // XX Test
  if(indenter("Python")) {
    std::cout << "Python: " << indenter("Python")->global("triggerCharacters").isValid() << "\n";
    std::cout << "Python: " << indenter("Python")->function("triggerCharacters").isValid() << "\n";
    std::cout << "Python: " << indenter("Python")->global("blafldsjfklas").isValid() << "\n";
    std::cout << "Python: " << indenter("Python")->function("indent").isValid() << "\n";
  }
  if(indenter("C"))
    std::cout << "C: " << qPrintable(indenter("C")->url()) << "\n";
  if(indenter("lisp"))
    std::cout << "LISP: " << qPrintable(indenter("Lisp")->url()) << "\n";
  config.sync();
}

bool KateScriptManager::parseMetaInformation(const QString& url,
                                             QHash<QString, QString> &pairs)
{
  // a valid script file -must- have the following format:
  // The first line must contain the string 'kate-script'.
  // All following lines have to have the format 'key : value'. So the value
  // is separated by a colon. Leading non-letter characters are ignored, that
  // include C and C++ comments for example.
  // Parsing the header stops at the first line with no ':'.

  QFile file(QFile::encodeName(url));
  if(!file.open(QIODevice::ReadOnly)) {
    std::cerr << "Script parse error: Cannot open file " << qPrintable(url) << '\n';
    return false;
  }

  kDebug(13050) << "Update script: " << url;
  QTextStream ts(&file);
  ts.setCodec("UTF-8");
  if(!ts.readLine().contains("kate-script")) {
    std::cerr << "Script parse error: No header found in " << qPrintable(url) << '\n';
    file.close();
    return false;
  }

  QString line;
  while(!(line = ts.readLine()).isNull()) {
    int colon = line.indexOf(':');
    if(colon <= 0)
      break; // no colon -> end of header found

    // if -1 then 0. if >= 0, move after star.
    int start = 0; // start points to first letter. idea: skip '*' and '//'
    while(start < line.length() && !line.at(start).isLetter())
      ++start;

    QString key = line.mid(start, colon - start).trimmed();
    QString value = line.right(line.length() - (colon + 1)).trimmed();
    pairs[key] = value;

    kDebug(13050) << "KateScriptManager::parseMetaInformation: found pair: "
                  << "(" << key << " | " << value << ")";
  }
  file.close();
  return true;
}

/// Kate::Command stuff

bool KateScriptManager::exec(KTextEditor::View *view, const QString &_cmd, QString &errorMsg)
{
  QStringList args(_cmd.split(QRegExp("\\s+"), QString::SkipEmptyParts));
  QString cmd(args.first());
  args.removeFirst();
#if 0
  if(!view) {
    errorMsg = i18n("Could not access view");
    return false;
  }

  KateView* kateView = qobject_cast<KateView*>(view);

  if(cmd == QLatin1String("js-run-myself"))
  {
    KateJSInterpreterContext script("");
    return script.evalSource(kateView, kateView->doc()->text(), errorMsg);
  }

  KateJScriptManager::Script *script = m_function2Script.value(cmd);

  if(!script) {
    errorMsg = i18n("Command not found: %1", cmd);
    return false;
  }

  KateJSInterpreterContext *inter = interpreter(script->basename);

  if(!inter)
  {
    errorMsg = i18n("Failed to start interpreter for script %1, command %2", script->basename, cmd);
    return false;
  }

  KJS::List params;

  foreach(const QString &a, args)
    params.append(KJS::jsString(a));

  KJS::JSValue *val = inter->callFunction(kateView, inter->interpreter()->globalObject(), KJS::Identifier(cmd),
                                   params, errorMsg);
#else
  if(!view) {
    errorMsg = i18n("Could not access view");
    return false;
  }
  errorMsg = i18n("Command not found: %1", cmd);
  return false;
#endif
}

bool KateScriptManager::help(KTextEditor::View *, const QString &cmd, QString &msg)
{
#if 0
  if (cmd == "js-run-myself") {
    msg = i18n("This executes the current document as JavaScript within Kate.");
    return true;
  }

  if (!m_scripts.contains(cmd))
    return false;

  msg = m_scripts[cmd]->help;

  return !msg.isEmpty();
#endif
  return true;
}

const QStringList &KateScriptManager::cmds()
{
  static QStringList l;
#if 0
  l.clear();
  l << "js-run-myself";

  QHashIterator<QString, KateJScriptManager::Script*> i(m_function2Script);
  while (i.hasNext()) {
      i.next();
      l << i.key();
  }

#endif
  return l;
}

// kate: space-indent on; indent-width 2; replace-tabs on;
<|endoftext|>


<commit_before>/*
-----------------------------------------------------------------------------
Copyright (c) 2011 Joachim Dorner

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 "Common.h"
#include "Connection.h"
#include "Function.h"

Connection::Connection( v8::Handle<v8::Object> thisHandle) :
  loginParamsSize(0),
  loginParams(nullptr),
  connectionHandle(nullptr)
{
  uv_mutex_init(&this->invocationMutex);
  init( thisHandle);
}

Connection::~Connection()
{
  deferLog(Levels::SILLY, "Connection::~Connection");

  if (this->connectionHandle != nullptr) {
    RFC_RC rc = RfcCloseConnection(this->connectionHandle, &errorInfo);
    DEFER_LOG_API(this, "RfcCloseConnection");
    if (rc != RFC_OK) {
      deferLog(Levels::DBG, "Connection::CloseConnection: Error closing connection");
    }
  }

  uv_mutex_destroy(&this->invocationMutex);

  for (unsigned int i = 0; i < this->loginParamsSize; i++) {
     free(const_cast<SAP_UC*>(loginParams[i].name));
     free(const_cast<SAP_UC*>(loginParams[i].value));
  }
  free(loginParams);

  delete this->cbOpen;
  this->cbOpen = nullptr;
  deferLog(Levels::SILLY, "Connection::~Connection [end]");
}

NAN_METHOD(Connection::New)
{
  if (!info.IsConstructCall()) {
    Nan::ThrowError("Invalid call format. Please use the 'new' operator.");
    return;
  }

  Connection *self = new Connection( info.This());

  self->log(Levels::SILLY, "Connection object created");

  info.GetReturnValue().Set(info.This());
}

NAN_MODULE_INIT(Connection::Init)
{
  Nan::HandleScope scope;

  v8::Local<v8::FunctionTemplate> ctorTemplate = Nan::New<v8::FunctionTemplate>(New);
  ctorTemplate->InstanceTemplate()->SetInternalFieldCount(1);
  ctorTemplate->SetClassName(Nan::New("Connection").ToLocalChecked());

  Nan::SetPrototypeMethod(ctorTemplate, "GetVersion", Connection::GetVersion);
  Nan::SetPrototypeMethod(ctorTemplate, "Open", Connection::Open);
  Nan::SetPrototypeMethod(ctorTemplate, "Close", Connection::Close);
  Nan::SetPrototypeMethod(ctorTemplate, "Ping", Connection::Ping);
  Nan::SetPrototypeMethod(ctorTemplate, "IsOpen", Connection::IsOpen);
  Nan::SetPrototypeMethod(ctorTemplate, "Lookup", Connection::Lookup);
  Nan::SetPrototypeMethod(ctorTemplate, "SetIniPath", Connection::SetIniPath);

  Nan::Set(target, Nan::New("Connection").ToLocalChecked(), ctorTemplate->GetFunction());
}

/**
 * @return Array
 */
NAN_METHOD(Connection::GetVersion)
{
  unsigned majorVersion, minorVersion, patchLevel;

  RfcGetVersion(&majorVersion, &minorVersion, &patchLevel);
  v8::Local<v8::Array> versionInfo = Nan::New<v8::Array>(3);

  versionInfo->Set(0, Nan::New<v8::Integer>(majorVersion));
  versionInfo->Set(1, Nan::New<v8::Integer>(minorVersion));
  versionInfo->Set(2, Nan::New<v8::Integer>(patchLevel));

  info.GetReturnValue().Set(versionInfo);
}

NAN_METHOD(Connection::Open)
{
  Connection *self = node::ObjectWrap::Unwrap<Connection>(info.This());

  self->log(Levels::VERBOSE, "opening new SAP connection");

  if (info.Length() < 2) {
    Nan::ThrowError("Function expects 2 arguments");
    return;
  }
  if (!info[0]->IsObject()) {
    Nan::ThrowError("Argument 1 must be an object");
    return;
  }
  if (!info[1]->IsFunction()) {
    Nan::ThrowError("Argument 2 must be a function");
    return;
  }

  v8::Local<v8::Object> optionsObj = info[0]->ToObject();
  v8::Local<v8::Array> props = optionsObj->GetPropertyNames();

  self->loginParamsSize = props->Length();
  self->loginParams = static_cast<RFC_CONNECTION_PARAMETER*>(malloc(self->loginParamsSize * sizeof(RFC_CONNECTION_PARAMETER)));
  memset(self->loginParams, 0, self->loginParamsSize * sizeof(RFC_CONNECTION_PARAMETER));
  memset(&self->errorInfo, 0, sizeof(RFC_ERROR_INFO));

  self->log(Levels::DBG, "Connection params", optionsObj);

  for (unsigned int i = 0; i < self->loginParamsSize; i++) {
    v8::Local<v8::Value> name = props->Get(i);
    v8::Local<v8::Value> value = optionsObj->Get(name->ToString());

    self->loginParams[i].name = convertToSAPUC(name);
    self->loginParams[i].value = convertToSAPUC(value);

#ifndef NDEBUG
    std::cout << convertToString(name) << "--> " << convertToString(value) << std::endl;
#endif
  }

  // Store callback
  self->cbOpen = new Nan::Callback(v8::Local<v8::Function>::Cast(info[1]));
  self->Ref();

  uv_work_t* req = new uv_work_t();
  req->data = self;
  uv_queue_work(uv_default_loop(), req, EIO_Open, (uv_after_work_cb)EIO_AfterOpen);
#if !NODE_VERSION_AT_LEAST(0, 7, 9)
    uv_ref(uv_default_loop());
#endif
}

void Connection::EIO_Open(uv_work_t *req)
{
  Connection *self = static_cast<Connection*>(req->data);

  self->connectionHandle = RfcOpenConnection(self->loginParams, self->loginParamsSize, &self->errorInfo);
  DEFER_LOG_API(self, "RfcOpenConnection");
}

void Connection::EIO_AfterOpen(uv_work_t *req)
{
  Nan::HandleScope scope;
  int isValid;
  Connection *self = static_cast<Connection*>(req->data);

  v8::Local<v8::Value> argv[1];
  argv[0] = Nan::Null();

  if (self->connectionHandle == nullptr) {
    self->log(Levels::DBG, "Connection handle is NULL, connection failed");
    argv[0] = RfcError(self->errorInfo);
  } else {
    RfcIsConnectionHandleValid(self->connectionHandle, &isValid, &self->errorInfo);
    LOG_API(self, "RfcIsConnectionHandleValid");
    if (!isValid) {
      self->log(Levels::SILLY, "Connection not valid");
      argv[0] = RfcError(self->errorInfo);
    } else {
      self->log(Levels::SILLY, "Connection still valid");
    }
  }

  Nan::TryCatch try_catch;

  self->log(Levels::SILLY, "EIO_AfterOpen: About to call the callback");
  assert(!self->cbOpen->IsEmpty());
  self->cbOpen->Call(1, argv);
  delete self->cbOpen;
  self->cbOpen = nullptr;
  self->Unref();
  self->log(Levels::SILLY, "EIO_AfterOpen: Finished callback");

  if (try_catch.HasCaught()) {
    self->log(Levels::ERR, "EIO_AfterOpen: Exception thrown in callback. Abort.");
    Nan::FatalException(try_catch);
  }
}

NAN_METHOD(Connection::Close)
{
  Connection *self = node::ObjectWrap::Unwrap<Connection>(info.This());
  self->log(Levels::SILLY, "Connection::Close");
  info.GetReturnValue().Set(self->CloseConnection());
}

v8::Local<v8::Value> Connection::CloseConnection(void)
{
  Nan::EscapableHandleScope scope;
  RFC_RC rc = RFC_OK;

  log(Levels::SILLY, "Connection::CloseConnection");

  if (this->connectionHandle != nullptr) {
    rc = RfcCloseConnection(this->connectionHandle, &errorInfo);
    LOG_API(this, "RfcCloseConnection");
    if (rc != RFC_OK) {
      log(Levels::DBG, "Connection::CloseConnection: Error closing connection");
      return scope.Escape(RfcError(errorInfo));
    }
  }

  return scope.Escape(Nan::True());
}

RFC_CONNECTION_HANDLE Connection::GetConnectionHandle(void)
{
  return this->connectionHandle;
}

void Connection::LockMutex(void)
{
  uv_mutex_lock(&this->invocationMutex);
}

void Connection::UnlockMutex(void)
{
  uv_mutex_unlock(&this->invocationMutex);
}

void Connection::addObjectInfoToLogMeta(v8::Local<v8::Object> meta)
{
  char ptr[ 2 + sizeof(void*)*2 + 1]; // optional "0x" + each byte of pointer represented by 2 digits + terminator
  snprintf( ptr, 2 + sizeof(void*)*2 + 1, "%p", this);
  meta->Set(Nan::New<v8::String>("nativeConnection").ToLocalChecked(),
            Nan::New<v8::String>(ptr).ToLocalChecked());
}

NAN_METHOD(Connection::IsOpen)
{
  Connection *self = node::ObjectWrap::Unwrap<Connection>(info.This());
  RFC_RC rc = RFC_OK;
  int isValid;

  self->log(Levels::SILLY, "Connection::IsOpen");

  rc = RfcIsConnectionHandleValid(self->connectionHandle, &isValid, &self->errorInfo);
  LOG_API(self, "RfcIsConnectionHandleValid");
  if(!isValid) {
    self->log(Levels::SILLY, "Connection::IsOpen: RfcIsConnectionHandleValid returned false");
    info.GetReturnValue().Set(Nan::False());
  } else {
    self->log(Levels::SILLY, "Connection::IsOpen: RfcIsConnectionHandleValid returned true");
    info.GetReturnValue().Set(Nan::True());
  }

}

/**
 *
 * @return true if successful, else: RfcException
 */
NAN_METHOD(Connection::Ping)
{
  Connection *self = node::ObjectWrap::Unwrap<Connection>(info.This());
  RFC_RC rc = RFC_OK;

  self->log(Levels::SILLY, "Connection::IsOpen");

  if (info.Length() > 0) {
    Nan::ThrowError("No arguments expected");
    return;
  }

  rc = RfcPing(self->connectionHandle, &self->errorInfo);
  LOG_API(self, "RfcPing");
  if (rc != RFC_OK) {
    RETURN_RFC_ERROR(self->errorInfo);
  }

  info.GetReturnValue().Set(Nan::True());
}

/**
 *
 * @return Function
 */
NAN_METHOD(Connection::Lookup)
{
  int isValid;

  Connection *self = node::ObjectWrap::Unwrap<Connection>(info.This());

  self->log(Levels::SILLY, "Connection::Lookup");

  if (info.Length() < 1 || info.Length() > 2) {
    Nan::ThrowError("Function expects 1 or 2 arguments");
    return;
  }
  if (!info[0]->IsString()) {
    Nan::ThrowError("Argument 1 must be function module name");
    return;
  }

  if (info.Length() > 1 && !info[1]->IsObject()) {
    Nan::ThrowError("Argument 2 must be an object");
    return;
  }

  RfcIsConnectionHandleValid(self->connectionHandle, &isValid, &self->errorInfo);
  LOG_API(self, "RfcIsConnectionHandleValid");
  if (!isValid) {
    self->log(Levels::SILLY, "Connection::Lookup: RfcIsConnectionHandleValid returned false");
    Nan::ThrowError(RfcError(self->errorInfo));
    return;
  } else {
    self->log(Levels::SILLY, "Connection::Lookup: RfcIsConnectionHandleValid returned true");
  }

  self->log(Levels::SILLY, "Connection::Lookup: About to create function instance");
  v8::Local<v8::Value> f = Function::NewInstance(*self, info);
  if( IsException(f)) {
    self->log(Levels::DBG, "Connection::Lookup: Unable to create function instance");
    Nan::ThrowError(f);
  }
  info.GetReturnValue().Set(f);
}

/**
 *
 * @return true if successful, else: RfcException
 */
NAN_METHOD(Connection::SetIniPath)
{
  Connection *self = node::ObjectWrap::Unwrap<Connection>(info.This());

  self->log(Levels::SILLY, "Connection::SetIniPath");

  if (info.Length() != 1) {
    Nan::ThrowError("Function expects 1 argument");
    return;
  }
  if (!info[0]->IsString()) {
    Nan::ThrowError("Argument 1 must be a path name");
    return;
  }

  v8::Local<v8::Value> iniPath = info[0]->ToString();

  RfcSetIniPath(convertToSAPUC(iniPath), &self->errorInfo);
  LOG_API(self, "RfcSetIniPath");
  if (self->errorInfo.code) {
    self->log(Levels::DBG, "Connection::SetIniPath: RfcSetIniPath failed");
    Nan::ThrowError(RfcError(self->errorInfo));
    return;
  }

  info.GetReturnValue().Set(Nan::True());
}
<commit_msg>RFS-102: reset connection handle when closing<commit_after>/*
-----------------------------------------------------------------------------
Copyright (c) 2011 Joachim Dorner

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 "Common.h"
#include "Connection.h"
#include "Function.h"

Connection::Connection( v8::Handle<v8::Object> thisHandle) :
  loginParamsSize(0),
  loginParams(nullptr),
  connectionHandle(nullptr)
{
  uv_mutex_init(&this->invocationMutex);
  init( thisHandle);
}

Connection::~Connection()
{
  deferLog(Levels::SILLY, "Connection::~Connection");

  if (this->connectionHandle != nullptr) {
    RFC_RC rc = RfcCloseConnection(this->connectionHandle, &errorInfo);
    DEFER_LOG_API(this, "RfcCloseConnection");
    if (rc != RFC_OK) {
      deferLog(Levels::DBG, "Connection::CloseConnection: Error closing connection");
    }
  }

  uv_mutex_destroy(&this->invocationMutex);

  for (unsigned int i = 0; i < this->loginParamsSize; i++) {
     free(const_cast<SAP_UC*>(loginParams[i].name));
     free(const_cast<SAP_UC*>(loginParams[i].value));
  }
  free(loginParams);

  delete this->cbOpen;
  this->cbOpen = nullptr;
  deferLog(Levels::SILLY, "Connection::~Connection [end]");
}

NAN_METHOD(Connection::New)
{
  if (!info.IsConstructCall()) {
    Nan::ThrowError("Invalid call format. Please use the 'new' operator.");
    return;
  }

  Connection *self = new Connection( info.This());

  self->log(Levels::SILLY, "Connection object created");

  info.GetReturnValue().Set(info.This());
}

NAN_MODULE_INIT(Connection::Init)
{
  Nan::HandleScope scope;

  v8::Local<v8::FunctionTemplate> ctorTemplate = Nan::New<v8::FunctionTemplate>(New);
  ctorTemplate->InstanceTemplate()->SetInternalFieldCount(1);
  ctorTemplate->SetClassName(Nan::New("Connection").ToLocalChecked());

  Nan::SetPrototypeMethod(ctorTemplate, "GetVersion", Connection::GetVersion);
  Nan::SetPrototypeMethod(ctorTemplate, "Open", Connection::Open);
  Nan::SetPrototypeMethod(ctorTemplate, "Close", Connection::Close);
  Nan::SetPrototypeMethod(ctorTemplate, "Ping", Connection::Ping);
  Nan::SetPrototypeMethod(ctorTemplate, "IsOpen", Connection::IsOpen);
  Nan::SetPrototypeMethod(ctorTemplate, "Lookup", Connection::Lookup);
  Nan::SetPrototypeMethod(ctorTemplate, "SetIniPath", Connection::SetIniPath);

  Nan::Set(target, Nan::New("Connection").ToLocalChecked(), ctorTemplate->GetFunction());
}

/**
 * @return Array
 */
NAN_METHOD(Connection::GetVersion)
{
  unsigned majorVersion, minorVersion, patchLevel;

  RfcGetVersion(&majorVersion, &minorVersion, &patchLevel);
  v8::Local<v8::Array> versionInfo = Nan::New<v8::Array>(3);

  versionInfo->Set(0, Nan::New<v8::Integer>(majorVersion));
  versionInfo->Set(1, Nan::New<v8::Integer>(minorVersion));
  versionInfo->Set(2, Nan::New<v8::Integer>(patchLevel));

  info.GetReturnValue().Set(versionInfo);
}

NAN_METHOD(Connection::Open)
{
  Connection *self = node::ObjectWrap::Unwrap<Connection>(info.This());

  self->log(Levels::VERBOSE, "opening new SAP connection");

  if (info.Length() < 2) {
    Nan::ThrowError("Function expects 2 arguments");
    return;
  }
  if (!info[0]->IsObject()) {
    Nan::ThrowError("Argument 1 must be an object");
    return;
  }
  if (!info[1]->IsFunction()) {
    Nan::ThrowError("Argument 2 must be a function");
    return;
  }

  v8::Local<v8::Object> optionsObj = info[0]->ToObject();
  v8::Local<v8::Array> props = optionsObj->GetPropertyNames();

  self->loginParamsSize = props->Length();
  self->loginParams = static_cast<RFC_CONNECTION_PARAMETER*>(malloc(self->loginParamsSize * sizeof(RFC_CONNECTION_PARAMETER)));
  memset(self->loginParams, 0, self->loginParamsSize * sizeof(RFC_CONNECTION_PARAMETER));
  memset(&self->errorInfo, 0, sizeof(RFC_ERROR_INFO));

  self->log(Levels::DBG, "Connection params", optionsObj);

  for (unsigned int i = 0; i < self->loginParamsSize; i++) {
    v8::Local<v8::Value> name = props->Get(i);
    v8::Local<v8::Value> value = optionsObj->Get(name->ToString());

    self->loginParams[i].name = convertToSAPUC(name);
    self->loginParams[i].value = convertToSAPUC(value);

#ifndef NDEBUG
    std::cout << convertToString(name) << "--> " << convertToString(value) << std::endl;
#endif
  }

  // Store callback
  self->cbOpen = new Nan::Callback(v8::Local<v8::Function>::Cast(info[1]));
  self->Ref();

  uv_work_t* req = new uv_work_t();
  req->data = self;
  uv_queue_work(uv_default_loop(), req, EIO_Open, (uv_after_work_cb)EIO_AfterOpen);
#if !NODE_VERSION_AT_LEAST(0, 7, 9)
    uv_ref(uv_default_loop());
#endif
}

void Connection::EIO_Open(uv_work_t *req)
{
  Connection *self = static_cast<Connection*>(req->data);

  self->connectionHandle = RfcOpenConnection(self->loginParams, self->loginParamsSize, &self->errorInfo);
  DEFER_LOG_API(self, "RfcOpenConnection");
}

void Connection::EIO_AfterOpen(uv_work_t *req)
{
  Nan::HandleScope scope;
  int isValid;
  Connection *self = static_cast<Connection*>(req->data);

  v8::Local<v8::Value> argv[1];
  argv[0] = Nan::Null();

  if (self->connectionHandle == nullptr) {
    self->log(Levels::DBG, "Connection handle is NULL, connection failed");
    argv[0] = RfcError(self->errorInfo);
  } else {
    RfcIsConnectionHandleValid(self->connectionHandle, &isValid, &self->errorInfo);
    LOG_API(self, "RfcIsConnectionHandleValid");
    if (!isValid) {
      self->log(Levels::SILLY, "Connection not valid");
      argv[0] = RfcError(self->errorInfo);
    } else {
      self->log(Levels::SILLY, "Connection still valid");
    }
  }

  Nan::TryCatch try_catch;

  self->log(Levels::SILLY, "EIO_AfterOpen: About to call the callback");
  assert(!self->cbOpen->IsEmpty());
  self->cbOpen->Call(1, argv);
  delete self->cbOpen;
  self->cbOpen = nullptr;
  self->Unref();
  self->log(Levels::SILLY, "EIO_AfterOpen: Finished callback");

  if (try_catch.HasCaught()) {
    self->log(Levels::ERR, "EIO_AfterOpen: Exception thrown in callback. Abort.");
    Nan::FatalException(try_catch);
  }
}

NAN_METHOD(Connection::Close)
{
  Connection *self = node::ObjectWrap::Unwrap<Connection>(info.This());
  self->log(Levels::SILLY, "Connection::Close");
  info.GetReturnValue().Set(self->CloseConnection());
}

v8::Local<v8::Value> Connection::CloseConnection(void)
{
  Nan::EscapableHandleScope scope;
  RFC_RC rc = RFC_OK;

  log(Levels::SILLY, "Connection::CloseConnection");

  if (this->connectionHandle != nullptr) {
    rc = RfcCloseConnection(this->connectionHandle, &errorInfo);
    this->connectionHandle = nullptr;
    LOG_API(this, "RfcCloseConnection");
    if (rc != RFC_OK) {
      log(Levels::DBG, "Connection::CloseConnection: Error closing connection");
      return scope.Escape(RfcError(errorInfo));
    }
  }

  return scope.Escape(Nan::True());
}

RFC_CONNECTION_HANDLE Connection::GetConnectionHandle(void)
{
  return this->connectionHandle;
}

void Connection::LockMutex(void)
{
  uv_mutex_lock(&this->invocationMutex);
}

void Connection::UnlockMutex(void)
{
  uv_mutex_unlock(&this->invocationMutex);
}

void Connection::addObjectInfoToLogMeta(v8::Local<v8::Object> meta)
{
  char ptr[ 2 + sizeof(void*)*2 + 1]; // optional "0x" + each byte of pointer represented by 2 digits + terminator
  snprintf( ptr, 2 + sizeof(void*)*2 + 1, "%p", this);
  meta->Set(Nan::New<v8::String>("nativeConnection").ToLocalChecked(),
            Nan::New<v8::String>(ptr).ToLocalChecked());
}

NAN_METHOD(Connection::IsOpen)
{
  Connection *self = node::ObjectWrap::Unwrap<Connection>(info.This());
  RFC_RC rc = RFC_OK;
  int isValid;

  self->log(Levels::SILLY, "Connection::IsOpen");

  rc = RfcIsConnectionHandleValid(self->connectionHandle, &isValid, &self->errorInfo);
  LOG_API(self, "RfcIsConnectionHandleValid");
  if(!isValid) {
    self->log(Levels::SILLY, "Connection::IsOpen: RfcIsConnectionHandleValid returned false");
    info.GetReturnValue().Set(Nan::False());
  } else {
    self->log(Levels::SILLY, "Connection::IsOpen: RfcIsConnectionHandleValid returned true");
    info.GetReturnValue().Set(Nan::True());
  }

}

/**
 *
 * @return true if successful, else: RfcException
 */
NAN_METHOD(Connection::Ping)
{
  Connection *self = node::ObjectWrap::Unwrap<Connection>(info.This());
  RFC_RC rc = RFC_OK;

  self->log(Levels::SILLY, "Connection::IsOpen");

  if (info.Length() > 0) {
    Nan::ThrowError("No arguments expected");
    return;
  }

  rc = RfcPing(self->connectionHandle, &self->errorInfo);
  LOG_API(self, "RfcPing");
  if (rc != RFC_OK) {
    RETURN_RFC_ERROR(self->errorInfo);
  }

  info.GetReturnValue().Set(Nan::True());
}

/**
 *
 * @return Function
 */
NAN_METHOD(Connection::Lookup)
{
  int isValid;

  Connection *self = node::ObjectWrap::Unwrap<Connection>(info.This());

  self->log(Levels::SILLY, "Connection::Lookup");

  if (info.Length() < 1 || info.Length() > 2) {
    Nan::ThrowError("Function expects 1 or 2 arguments");
    return;
  }
  if (!info[0]->IsString()) {
    Nan::ThrowError("Argument 1 must be function module name");
    return;
  }

  if (info.Length() > 1 && !info[1]->IsObject()) {
    Nan::ThrowError("Argument 2 must be an object");
    return;
  }

  RfcIsConnectionHandleValid(self->connectionHandle, &isValid, &self->errorInfo);
  LOG_API(self, "RfcIsConnectionHandleValid");
  if (!isValid) {
    self->log(Levels::SILLY, "Connection::Lookup: RfcIsConnectionHandleValid returned false");
    Nan::ThrowError(RfcError(self->errorInfo));
    return;
  } else {
    self->log(Levels::SILLY, "Connection::Lookup: RfcIsConnectionHandleValid returned true");
  }

  self->log(Levels::SILLY, "Connection::Lookup: About to create function instance");
  v8::Local<v8::Value> f = Function::NewInstance(*self, info);
  if( IsException(f)) {
    self->log(Levels::DBG, "Connection::Lookup: Unable to create function instance");
    Nan::ThrowError(f);
  }
  info.GetReturnValue().Set(f);
}

/**
 *
 * @return true if successful, else: RfcException
 */
NAN_METHOD(Connection::SetIniPath)
{
  Connection *self = node::ObjectWrap::Unwrap<Connection>(info.This());

  self->log(Levels::SILLY, "Connection::SetIniPath");

  if (info.Length() != 1) {
    Nan::ThrowError("Function expects 1 argument");
    return;
  }
  if (!info[0]->IsString()) {
    Nan::ThrowError("Argument 1 must be a path name");
    return;
  }

  v8::Local<v8::Value> iniPath = info[0]->ToString();

  RfcSetIniPath(convertToSAPUC(iniPath), &self->errorInfo);
  LOG_API(self, "RfcSetIniPath");
  if (self->errorInfo.code) {
    self->log(Levels::DBG, "Connection::SetIniPath: RfcSetIniPath failed");
    Nan::ThrowError(RfcError(self->errorInfo));
    return;
  }

  info.GetReturnValue().Set(Nan::True());
}
<|endoftext|>


<commit_before><commit_msg>update context infos of paths with separators<commit_after><|endoftext|>


<commit_before><commit_msg>Bad fix<commit_after><|endoftext|>


<commit_before>/// JSON encoding and decoding
// Encoding Lua tables to JSON strings, and decoding JSON strings to Lua tables.
// @module json

#include "JSON.h"
#include <Poco/JSON/Parser.h>
#include <Poco/JSON/Handler.h>
#include <Poco/JSON/PrintHandler.h>
#include <Poco/JSON/JSONException.h>
#include <Poco/JSONString.h>
#include <Poco/SharedPtr.h>
#include "Userdata.h"
#include "LuaPocoUtils.h"

int luaopen_poco_json(lua_State* L)
{
    struct LuaPoco::UserdataMethod methods[] =
    {
        { "encode", LuaPoco::JSON::encode },
        { "decode", LuaPoco::JSON::decode },
        { "null", LuaPoco::JSON::getNull },
        { "emptyObject", LuaPoco::JSON::getEmptyObject },
        { "emptyArray", LuaPoco::JSON::getEmptyArray },
        { NULL, NULL}
    };

    lua_createtable(L, 0, 2);
    setMetatableFunctions(L, methods);

    return 1;
}

namespace LuaPoco
{

char jsonNull[] = "Poco.JSON.null";
char jsonEmptyArray[] = "Poco.JSON.Empty.Array";
char jsonEmptyObject[] = "Poco.JSON.Empty.Object";

struct TableInfo
{
    enum TableType { object, array} tableType;
    size_t currentArrayIndex;
    int tableStackIndex;
};

class LuaHandler : public Poco::JSON::Handler
{
public:

    LuaHandler(lua_State* L) : mState(L), mBaseTop(lua_gettop(L)) {}
    virtual ~LuaHandler() {}

    virtual void reset()
    {
        lua_settop(mState, mBaseTop);
    }

    virtual void startObject()
    {
        lua_newtable(mState);
        TableInfo ti = { TableInfo::TableType::object, 0, lua_gettop(mState) };
        mTableQueue.push_back(ti);
    }

    virtual void endObject()
    {
        mTableQueue.pop_back();
        setValue();
    }

    virtual void startArray()
    {
        lua_newtable(mState);
        TableInfo ti = { TableInfo::TableType::array, 0, lua_gettop(mState) };
        mTableQueue.push_back(ti);
    }

    virtual void endArray()
    {
        mTableQueue.pop_back();
        setValue();
    }

    virtual void key(const std::string& k)
    {
        // leave the key on the stack such that on a value, setValue is called to pop both off.
        lua_pushlstring(mState, k.c_str(), k.size());
    }

    virtual void null()
    {
        // use sentinel value jsonNull
        lua_pushlightuserdata(mState, static_cast<void*>(jsonNull));
        setValue();
    }

    // based on the poco code, int and unsigned are pure virtual functions, but will never be
    // called as ParserImpl only uses Int64s for whatever reason.  including for completeness.
    virtual void value(int v)
    {
        lua_Integer i = 0;
        if (checkSignedToLuaInteger<int>(v, i))
        {
            lua_pushinteger(mState, static_cast<lua_Integer>(i));
            setValue();
        }
        else throw Poco::JSON::JSONException("out of range number");
    }

    virtual void value(unsigned v)
    {
        lua_Integer i = 0;
        if (checkUnsignedToLuaInteger<unsigned>(v, i))
        {
            lua_pushinteger(mState, static_cast<lua_Integer>(i));
            setValue();
        }
        else throw Poco::JSON::JSONException("out of range number");
    }

#if defined(POCO_HAVE_INT64)
    virtual void value(Poco::Int64 v)
    {
        lua_Integer i = 0;
        if (checkSignedToLuaInteger<Poco::Int64>(v, i))
        {
            lua_pushinteger(mState, static_cast<lua_Integer>(i));
            setValue();
        }
        else throw Poco::JSON::JSONException("out of range number");
    }

    virtual void value(Poco::UInt64 v)
    {
        lua_Integer i = 0;
        if (checkUnsignedToLuaInteger<Poco::UInt64>(v, i))
        {
            lua_pushinteger(mState, static_cast<lua_Integer>(i));
            setValue();
        }
        else throw Poco::JSON::JSONException("out of range number");
    }
#endif

    virtual void value(const std::string& s)
    {
        lua_pushlstring(mState, s.c_str(), s.size());
        setValue();
    }

    virtual void value(double d)
    {
        lua_pushnumber(mState, static_cast<lua_Number>(d));
        setValue();
    }

    virtual void value(bool b)
    {
        lua_pushboolean(mState, static_cast<int>(b));
        setValue();
    }

private:
    // utility functions
    void setValue()
    {
        if (mTableQueue.size() > 0)
        {
            TableInfo& ti = mTableQueue.back();
            if (ti.tableType == TableInfo::TableType::array)
            {
                ++ti.currentArrayIndex;
                lua_rawseti(mState, ti.tableStackIndex, ti.currentArrayIndex);
            }
            else
            {
                const char *tn = lua_typename(mState, lua_type(mState, -1));
                lua_rawset(mState, ti.tableStackIndex);
            }
        }
        else
        {
            // the last value is a table that should be left behind and returned.
            if (lua_type(mState, -1) != LUA_TTABLE)
                throw Poco::JSON::JSONException("attempt to set a value when no objects present");
        }
    }

    lua_State* mState;
    int mBaseTop;

    std::vector<TableInfo> mTableQueue;
};

class TableEncoder
{
public:
    TableEncoder(lua_State* L, unsigned indent) : mState(L), mStream(), mPrintHandler(mStream, indent) {}
    ~TableEncoder() {}

    bool encode()
    {
        handleTable();

        while (mTableQueue.size() > 0)
        {
            TableInfo& ti = mTableQueue.back();
            if (ti.tableType == TableInfo::TableType::array)
            {
                lua_rawgeti(mState, ti.tableStackIndex, ++ti.currentArrayIndex);
                if (!lua_isnil(mState, -1)) { handleValue(); }
                else
                {
                    // pop nil, array
                    lua_pop(mState, 2);
                    mTableQueue.pop_back();
                    mPrintHandler.endArray();
                }
            }
            else
            {
                // handleTable() leaves nil on the stack to for the nested table iteration.
                if (lua_next(mState, ti.tableStackIndex))
                {
                    if (lua_isstring(mState, -2))
                    {
                        const char* key = lua_tostring(mState, -2);
                        mPrintHandler.key(key);
                        handleValue();
                    }
                    else throw Poco::JSON::JSONException("encountered key value that is not a string.");
                }
                else
                {
                    // done with object, pop it.
                    lua_pop(mState, 1);
                    mTableQueue.pop_back();
                    mPrintHandler.endObject();
                }
            }
        }

        std::string jsonString = mStream.str();
        lua_pushlstring(mState, jsonString.c_str(), jsonString.size());
        // return all pending tables were processed
        return mTableQueue.empty();
    }

private:
    // arrays are defined as tables that operate as sequences from 1 to n with [n + 1] == nil to terminate.
    // objects are defined as not having an array part, and only string keys being present.
    bool isArray(int index)
    {
        lua_rawgeti(mState, index, 1);
        bool result = !lua_isnil(mState, -1);
        lua_pop(mState, 1);
        return result;
    }

    void handleTable()
    {
        // store table information in mQueue.
        TableInfo ti =
        {
            isArray(-1) ? TableInfo::TableType::array : TableInfo::TableType::object,
            0,
            lua_gettop(mState)
        };
        mTableQueue.push_back(ti);

        // leave key on the top of the stack so the loop can process it with calls to lua_next.
        // inform printer of the start of a new object/array.
        if (ti.tableType == TableInfo::TableType::object)
        {
            mPrintHandler.startObject();
            lua_pushnil(mState);
        }
        else { mPrintHandler.startArray(); }
    }

    void handleValue()
    {
        int type = lua_type(mState, -1);

        switch (type)
        {
        case LUA_TNIL:
            throw Poco::JSON::JSONException("nil is an invalid value, use json.null");
            break;
        case LUA_TNUMBER:
        #if LUA_VERSION_NUM > 502
            if (lua_isinteger(mState, -1))
            {
                lua_Integer i = lua_tointeger(mState, -1);
                mPrintHandler.value(i);
            }
            else
        #endif
            {
                lua_Number n = lua_tonumber(mState, -1);
                mPrintHandler.value(n);
            }
            break;
        case LUA_TBOOLEAN:
        {
            bool b = static_cast<bool>(lua_toboolean(mState, -1));
            mPrintHandler.value(b);
            break;
        }
        case LUA_TSTRING:
        {
            const std::string str = lua_tostring(mState, -1);
            mPrintHandler.value(str);
            break;
        }
        case LUA_TTABLE:
            handleTable();
            // the table value must stay on the top of the stack in order to be iterated by
            // encode loop.  when the end of the table is encountered, it is popped.
            // returning here avoids the pop that is needed for all other values.
            return;
            break;
        case LUA_TFUNCTION:
            throw Poco::JSON::JSONException("function type invalid for json.");
            break;
        case LUA_TUSERDATA:
            throw Poco::JSON::JSONException("userdata type invalid for json.");
            break;
        case LUA_TTHREAD:
            throw Poco::JSON::JSONException("thread type invalid for json.");
            break;
        case LUA_TLIGHTUSERDATA:
        {
            const char *lud = static_cast<const char*>(lua_touserdata(mState, -1));

            if (lud == jsonNull) { mPrintHandler.null(); }
            else if (lud == jsonEmptyArray) { mPrintHandler.startArray(); mPrintHandler.endArray(); }
            else if (lud == jsonEmptyObject) { mPrintHandler.startObject(); mPrintHandler.endObject(); }
            else throw Poco::JSON::JSONException("unknown json lightuserdata value.");
            break;
        }
        default:
            throw Poco::JSON::JSONException("unknown value for json conversion.");
            break;
        }

        // all values except for table needs to be popped.
        // the table case returns early to avoid this pop.
        lua_pop(mState, 1);
    }

    lua_State* mState;
    std::stringstream mStream;
    Poco::JSON::PrintHandler mPrintHandler;
    std::vector<TableInfo> mTableQueue;
};

/// encodes a table into a JSON string.
// @table table to encode
// @return value as string or nil. (error)
// @return error message.
// @function encode
int JSON::encode(lua_State* L)
{
    int rv = 0;
    unsigned indent = 0;

    luaL_checktype(L, 1, LUA_TTABLE);
    if (lua_isnumber(L, 2)) { indent = static_cast<unsigned>(lua_tointeger(L, 2)); }
    // TableEncoder expects the table it is encoding at the top of the stack.
    lua_pushvalue(L, 1);

    try
    {
        TableEncoder te(L, indent);
        // either a string is returned at the top of the stack
        // or nil, errmsg
        if (te.encode()) { rv = 1; }
        else { rv = 2; }
    }
    catch (const Poco::Exception& e)
    {
        rv = pushPocoException(L, e);
    }
    catch (...)
    {
        rv = pushUnknownException(L);
    }

    return rv;
}

/// decodes a JSON string into a table.
// @string JSON encoded string
// @return table or nil. (error)
// @return error message.
// @function decode
int JSON::decode(lua_State* L)
{
    int rv = 0;
    const char* jss = luaL_checkstring(L, 1);

    try
    {
        Poco::SharedPtr<LuaHandler> lh(new LuaHandler(L));
        Poco::JSON::Parser jsonParser(lh);
        jsonParser.parse(jss);

        rv = 1;
    }
    catch (const Poco::Exception& e)
    {
        rv = pushPocoException(L, e);
    }
    catch (...)
    {
        rv = pushUnknownException(L);
    }

    return rv;
}

/// returns the 'null' sentinel value as a lightuserdata.
// @return lightuserdata
// @function null
int JSON::getNull(lua_State* L)
{
    lua_pushlightuserdata(L, static_cast<void*>(jsonNull));
    return 1;
}

/// returns the 'emptyObject' sentinel value as a lightuserdata.
// @return lightuserdata
// @function emptyObject
int JSON::getEmptyObject(lua_State* L)
{
    lua_pushlightuserdata(L, static_cast<void*>(jsonEmptyObject));
    return 1;
}

/// returns the 'emptyArray' sentinel value as a lightuserdata.
// @return lightuserdata
// @function emptyArray
int JSON::getEmptyArray(lua_State* L)
{
    lua_pushlightuserdata(L, static_cast<void*>(jsonEmptyArray));
    return 1;
}

} // LuaPoco
<commit_msg>rename class LuaHandler to JsonDecoder.<commit_after>/// JSON encoding and decoding
// Encoding Lua tables to JSON strings, and decoding JSON strings to Lua tables.
// @module json

#include "JSON.h"
#include <Poco/JSON/Parser.h>
#include <Poco/JSON/Handler.h>
#include <Poco/JSON/PrintHandler.h>
#include <Poco/JSON/JSONException.h>
#include <Poco/JSONString.h>
#include <Poco/SharedPtr.h>
#include "Userdata.h"
#include "LuaPocoUtils.h"

int luaopen_poco_json(lua_State* L)
{
    struct LuaPoco::UserdataMethod methods[] =
    {
        { "encode", LuaPoco::JSON::encode },
        { "decode", LuaPoco::JSON::decode },
        { "null", LuaPoco::JSON::getNull },
        { "emptyObject", LuaPoco::JSON::getEmptyObject },
        { "emptyArray", LuaPoco::JSON::getEmptyArray },
        { NULL, NULL}
    };

    lua_createtable(L, 0, 2);
    setMetatableFunctions(L, methods);

    return 1;
}

namespace LuaPoco
{

char jsonNull[] = "Poco.JSON.null";
char jsonEmptyArray[] = "Poco.JSON.Empty.Array";
char jsonEmptyObject[] = "Poco.JSON.Empty.Object";

struct TableInfo
{
    enum TableType { object, array} tableType;
    size_t currentArrayIndex;
    int tableStackIndex;
};

class JsonDecoder : public Poco::JSON::Handler
{
public:

    JsonDecoder(lua_State* L) : mState(L), mBaseTop(lua_gettop(L)) {}
    virtual ~JsonDecoder() {}

    virtual void reset()
    {
        lua_settop(mState, mBaseTop);
    }

    virtual void startObject()
    {
        lua_newtable(mState);
        TableInfo ti = { TableInfo::TableType::object, 0, lua_gettop(mState) };
        mTableQueue.push_back(ti);
    }

    virtual void endObject()
    {
        mTableQueue.pop_back();
        setValue();
    }

    virtual void startArray()
    {
        lua_newtable(mState);
        TableInfo ti = { TableInfo::TableType::array, 0, lua_gettop(mState) };
        mTableQueue.push_back(ti);
    }

    virtual void endArray()
    {
        mTableQueue.pop_back();
        setValue();
    }

    virtual void key(const std::string& k)
    {
        // leave the key on the stack such that on a value, setValue is called to pop both off.
        lua_pushlstring(mState, k.c_str(), k.size());
    }

    virtual void null()
    {
        // use sentinel value jsonNull
        lua_pushlightuserdata(mState, static_cast<void*>(jsonNull));
        setValue();
    }

    // based on the poco code, int and unsigned are pure virtual functions, but will never be
    // called as ParserImpl only uses Int64s for whatever reason.  including for completeness.
    virtual void value(int v)
    {
        lua_Integer i = 0;
        if (checkSignedToLuaInteger<int>(v, i))
        {
            lua_pushinteger(mState, static_cast<lua_Integer>(i));
            setValue();
        }
        else throw Poco::JSON::JSONException("out of range number");
    }

    virtual void value(unsigned v)
    {
        lua_Integer i = 0;
        if (checkUnsignedToLuaInteger<unsigned>(v, i))
        {
            lua_pushinteger(mState, static_cast<lua_Integer>(i));
            setValue();
        }
        else throw Poco::JSON::JSONException("out of range number");
    }

#if defined(POCO_HAVE_INT64)
    virtual void value(Poco::Int64 v)
    {
        lua_Integer i = 0;
        if (checkSignedToLuaInteger<Poco::Int64>(v, i))
        {
            lua_pushinteger(mState, static_cast<lua_Integer>(i));
            setValue();
        }
        else throw Poco::JSON::JSONException("out of range number");
    }

    virtual void value(Poco::UInt64 v)
    {
        lua_Integer i = 0;
        if (checkUnsignedToLuaInteger<Poco::UInt64>(v, i))
        {
            lua_pushinteger(mState, static_cast<lua_Integer>(i));
            setValue();
        }
        else throw Poco::JSON::JSONException("out of range number");
    }
#endif

    virtual void value(const std::string& s)
    {
        lua_pushlstring(mState, s.c_str(), s.size());
        setValue();
    }

    virtual void value(double d)
    {
        lua_pushnumber(mState, static_cast<lua_Number>(d));
        setValue();
    }

    virtual void value(bool b)
    {
        lua_pushboolean(mState, static_cast<int>(b));
        setValue();
    }

private:
    // utility functions
    void setValue()
    {
        if (mTableQueue.size() > 0)
        {
            TableInfo& ti = mTableQueue.back();
            if (ti.tableType == TableInfo::TableType::array)
            {
                ++ti.currentArrayIndex;
                lua_rawseti(mState, ti.tableStackIndex, ti.currentArrayIndex);
            }
            else
            {
                const char *tn = lua_typename(mState, lua_type(mState, -1));
                lua_rawset(mState, ti.tableStackIndex);
            }
        }
        else
        {
            // the last value is a table that should be left behind and returned.
            if (lua_type(mState, -1) != LUA_TTABLE)
                throw Poco::JSON::JSONException("attempt to set a value when no objects present");
        }
    }

    lua_State* mState;
    int mBaseTop;

    std::vector<TableInfo> mTableQueue;
};

class TableEncoder
{
public:
    TableEncoder(lua_State* L, unsigned indent) : mState(L), mStream(), mPrintHandler(mStream, indent) {}
    ~TableEncoder() {}

    bool encode()
    {
        handleTable();

        while (mTableQueue.size() > 0)
        {
            TableInfo& ti = mTableQueue.back();
            if (ti.tableType == TableInfo::TableType::array)
            {
                lua_rawgeti(mState, ti.tableStackIndex, ++ti.currentArrayIndex);
                if (!lua_isnil(mState, -1)) { handleValue(); }
                else
                {
                    // pop nil, array
                    lua_pop(mState, 2);
                    mTableQueue.pop_back();
                    mPrintHandler.endArray();
                }
            }
            else
            {
                // handleTable() leaves nil on the stack to for the nested table iteration.
                if (lua_next(mState, ti.tableStackIndex))
                {
                    if (lua_isstring(mState, -2))
                    {
                        const char* key = lua_tostring(mState, -2);
                        mPrintHandler.key(key);
                        handleValue();
                    }
                    else throw Poco::JSON::JSONException("encountered key value that is not a string.");
                }
                else
                {
                    // done with object, pop it.
                    lua_pop(mState, 1);
                    mTableQueue.pop_back();
                    mPrintHandler.endObject();
                }
            }
        }

        std::string jsonString = mStream.str();
        lua_pushlstring(mState, jsonString.c_str(), jsonString.size());
        // return all pending tables were processed
        return mTableQueue.empty();
    }

private:
    // arrays are defined as tables that operate as sequences from 1 to n with [n + 1] == nil to terminate.
    // objects are defined as not having an array part, and only string keys being present.
    bool isArray(int index)
    {
        lua_rawgeti(mState, index, 1);
        bool result = !lua_isnil(mState, -1);
        lua_pop(mState, 1);
        return result;
    }

    void handleTable()
    {
        // store table information in mQueue.
        TableInfo ti =
        {
            isArray(-1) ? TableInfo::TableType::array : TableInfo::TableType::object,
            0,
            lua_gettop(mState)
        };
        mTableQueue.push_back(ti);

        // leave key on the top of the stack so the loop can process it with calls to lua_next.
        // inform printer of the start of a new object/array.
        if (ti.tableType == TableInfo::TableType::object)
        {
            mPrintHandler.startObject();
            lua_pushnil(mState);
        }
        else { mPrintHandler.startArray(); }
    }

    void handleValue()
    {
        int type = lua_type(mState, -1);

        switch (type)
        {
        case LUA_TNIL:
            throw Poco::JSON::JSONException("nil is an invalid value, use json.null");
            break;
        case LUA_TNUMBER:
        #if LUA_VERSION_NUM > 502
            if (lua_isinteger(mState, -1))
            {
                lua_Integer i = lua_tointeger(mState, -1);
                mPrintHandler.value(i);
            }
            else
        #endif
            {
                lua_Number n = lua_tonumber(mState, -1);
                mPrintHandler.value(n);
            }
            break;
        case LUA_TBOOLEAN:
        {
            bool b = static_cast<bool>(lua_toboolean(mState, -1));
            mPrintHandler.value(b);
            break;
        }
        case LUA_TSTRING:
        {
            const std::string str = lua_tostring(mState, -1);
            mPrintHandler.value(str);
            break;
        }
        case LUA_TTABLE:
            handleTable();
            // the table value must stay on the top of the stack in order to be iterated by
            // encode loop.  when the end of the table is encountered, it is popped.
            // returning here avoids the pop that is needed for all other values.
            return;
            break;
        case LUA_TFUNCTION:
            throw Poco::JSON::JSONException("function type invalid for json.");
            break;
        case LUA_TUSERDATA:
            throw Poco::JSON::JSONException("userdata type invalid for json.");
            break;
        case LUA_TTHREAD:
            throw Poco::JSON::JSONException("thread type invalid for json.");
            break;
        case LUA_TLIGHTUSERDATA:
        {
            const char *lud = static_cast<const char*>(lua_touserdata(mState, -1));

            if (lud == jsonNull) { mPrintHandler.null(); }
            else if (lud == jsonEmptyArray) { mPrintHandler.startArray(); mPrintHandler.endArray(); }
            else if (lud == jsonEmptyObject) { mPrintHandler.startObject(); mPrintHandler.endObject(); }
            else throw Poco::JSON::JSONException("unknown json lightuserdata value.");
            break;
        }
        default:
            throw Poco::JSON::JSONException("unknown value for json conversion.");
            break;
        }

        // all values except for table needs to be popped.
        // the table case returns early to avoid this pop.
        lua_pop(mState, 1);
    }

    lua_State* mState;
    std::stringstream mStream;
    Poco::JSON::PrintHandler mPrintHandler;
    std::vector<TableInfo> mTableQueue;
};

/// encodes a table into a JSON string.
// @table table to encode
// @return value as string or nil. (error)
// @return error message.
// @function encode
int JSON::encode(lua_State* L)
{
    int rv = 0;
    unsigned indent = 0;

    luaL_checktype(L, 1, LUA_TTABLE);
    if (lua_isnumber(L, 2)) { indent = static_cast<unsigned>(lua_tointeger(L, 2)); }
    // TableEncoder expects the table it is encoding at the top of the stack.
    lua_pushvalue(L, 1);

    try
    {
        TableEncoder te(L, indent);
        // either a string is returned at the top of the stack
        // or nil, errmsg
        if (te.encode()) { rv = 1; }
        else { rv = 2; }
    }
    catch (const Poco::Exception& e)
    {
        rv = pushPocoException(L, e);
    }
    catch (...)
    {
        rv = pushUnknownException(L);
    }

    return rv;
}

/// decodes a JSON string into a table.
// @string JSON encoded string
// @return table or nil. (error)
// @return error message.
// @function decode
int JSON::decode(lua_State* L)
{
    int rv = 0;
    const char* jss = luaL_checkstring(L, 1);

    try
    {
        Poco::SharedPtr<JsonDecoder> lh(new JsonDecoder(L));
        Poco::JSON::Parser jsonParser(lh);
        jsonParser.parse(jss);

        rv = 1;
    }
    catch (const Poco::Exception& e)
    {
        rv = pushPocoException(L, e);
    }
    catch (...)
    {
        rv = pushUnknownException(L);
    }

    return rv;
}

/// returns the 'null' sentinel value as a lightuserdata.
// @return lightuserdata
// @function null
int JSON::getNull(lua_State* L)
{
    lua_pushlightuserdata(L, static_cast<void*>(jsonNull));
    return 1;
}

/// returns the 'emptyObject' sentinel value as a lightuserdata.
// @return lightuserdata
// @function emptyObject
int JSON::getEmptyObject(lua_State* L)
{
    lua_pushlightuserdata(L, static_cast<void*>(jsonEmptyObject));
    return 1;
}

/// returns the 'emptyArray' sentinel value as a lightuserdata.
// @return lightuserdata
// @function emptyArray
int JSON::getEmptyArray(lua_State* L)
{
    lua_pushlightuserdata(L, static_cast<void*>(jsonEmptyArray));
    return 1;
}

} // LuaPoco
<|endoftext|>


<commit_before>// Scintilla source code edit control
/** @file LexMatlab.cxx
 ** Lexer for Matlab.
 ** Written by Jos Fonseca
 **
 ** Changes by Christoph Dalitz 2003/12/04:
 **   - added support for Octave
 **   - Strings can now be included both in single or double quotes
 **/
// Copyright 1998-2001 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.

#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <stdio.h>
#include <stdarg.h>

#include "Platform.h"

#include "PropSet.h"
#include "Accessor.h"
#include "StyleContext.h"
#include "KeyWords.h"
#include "Scintilla.h"
#include "SciLexer.h"

static bool IsMatlabCommentChar(int c) {
	return (c == '%') ;
}

static bool IsOctaveCommentChar(int c) {
	return (c == '%' || c == '#') ;
}

static bool IsMatlabComment(Accessor &styler, int pos, int len) {
	return len > 0 && IsMatlabCommentChar(styler[pos]) ;
}

static bool IsOctaveComment(Accessor &styler, int pos, int len) {
	return len > 0 && IsOctaveCommentChar(styler[pos]) ;
}

static inline bool IsAWordChar(const int ch) {
	return (ch < 0x80) && (isalnum(ch) || ch == '_');
}

static inline bool IsAWordStart(const int ch) {
	return (ch < 0x80) && (isalnum(ch) || ch == '_');
}

static void ColouriseMatlabOctaveDoc(
            unsigned int startPos, int length, int initStyle,
            WordList *keywordlists[], Accessor &styler,
            bool (*IsCommentChar)(int)) {

	WordList &keywords = *keywordlists[0];

	styler.StartAt(startPos);

	bool transpose = false;

	StyleContext sc(startPos, length, initStyle, styler);

	for (; sc.More(); sc.Forward()) {

		if (sc.state == SCE_MATLAB_OPERATOR) {
			if (sc.chPrev == '.') {
				if (sc.ch == '*' || sc.ch == '/' || sc.ch == '\\' || sc.ch == '^') {
					sc.ForwardSetState(SCE_MATLAB_DEFAULT);
					transpose = false;
				} else if (sc.ch == '\'') {
					sc.ForwardSetState(SCE_MATLAB_DEFAULT);
					transpose = true;
				} else {
					sc.SetState(SCE_MATLAB_DEFAULT);
				}
			} else {
				sc.SetState(SCE_MATLAB_DEFAULT);
			}
		} else if (sc.state == SCE_MATLAB_KEYWORD) {
			if (!isalnum(sc.ch) && sc.ch != '_') {
				char s[100];
				sc.GetCurrentLowered(s, sizeof(s));
				if (keywords.InList(s)) {
					sc.SetState(SCE_MATLAB_DEFAULT);
					transpose = false;
				} else {
					sc.ChangeState(SCE_MATLAB_IDENTIFIER);
					sc.SetState(SCE_MATLAB_DEFAULT);
					transpose = true;
				}
			}
		} else if (sc.state == SCE_MATLAB_NUMBER) {
			if (!isdigit(sc.ch) && sc.ch != '.'
			        && !(sc.ch == 'e' || sc.ch == 'E')
			        && !((sc.ch == '+' || sc.ch == '-') && (sc.chPrev == 'e' || sc.chPrev == 'E'))) {
				sc.SetState(SCE_MATLAB_DEFAULT);
				transpose = true;
			}
		} else if (sc.state == SCE_MATLAB_STRING) {
			if (sc.ch == '\'' && sc.chPrev != '\\') {
				sc.ForwardSetState(SCE_MATLAB_DEFAULT);
			}
		} else if (sc.state == SCE_MATLAB_DOUBLEQUOTESTRING) {
			if (sc.ch == '"' && sc.chPrev != '\\') {
				sc.ForwardSetState(SCE_MATLAB_DEFAULT);
			}
		} else if (sc.state == SCE_MATLAB_COMMENT || sc.state == SCE_MATLAB_COMMAND) {
			if (sc.atLineEnd) {
				sc.SetState(SCE_MATLAB_DEFAULT);
				transpose = false;
			}
		}

		if (sc.state == SCE_MATLAB_DEFAULT) {
			if (IsCommentChar(sc.ch)) {
				sc.SetState(SCE_MATLAB_COMMENT);
			} else if (sc.ch == '!') {
				sc.SetState(SCE_MATLAB_COMMAND);
			} else if (sc.ch == '\'') {
				if (transpose) {
					sc.SetState(SCE_MATLAB_OPERATOR);
				} else {
					sc.SetState(SCE_MATLAB_STRING);
				}
			} else if (sc.ch == '"') {
              sc.SetState(SCE_MATLAB_DOUBLEQUOTESTRING);
			} else if (isdigit(sc.ch) || (sc.ch == '.' && isdigit(sc.chNext))) {
				sc.SetState(SCE_MATLAB_NUMBER);
			} else if (isalpha(sc.ch)) {
				sc.SetState(SCE_MATLAB_KEYWORD);
			} else if (isoperator(static_cast<char>(sc.ch)) || sc.ch == '@' || sc.ch == '\\') {
				if (sc.ch == ')' || sc.ch == ']') {
					transpose = true;
				} else {
					transpose = false;
				}
				sc.SetState(SCE_MATLAB_OPERATOR);
			} else {
				transpose = false;
			}
		}
	}
	sc.Complete();
}

static void ColouriseMatlabDoc(unsigned int startPos, int length, int initStyle,
                               WordList *keywordlists[], Accessor &styler) {
  ColouriseMatlabOctaveDoc(startPos, length, initStyle, keywordlists, styler, IsMatlabCommentChar);
}

static void ColouriseOctaveDoc(unsigned int startPos, int length, int initStyle,
                               WordList *keywordlists[], Accessor &styler) {
  ColouriseMatlabOctaveDoc(startPos, length, initStyle, keywordlists, styler, IsOctaveCommentChar);
}

static void FoldMatlabOctaveDoc(unsigned int startPos, int length, int,
                          WordList *[], Accessor &styler,
                          bool (*IsComment)(Accessor&,int,int)) {

	int endPos = startPos + length;

	// Backtrack to previous line in case need to fix its fold status
	int lineCurrent = styler.GetLine(startPos);
	if (startPos > 0) {
		if (lineCurrent > 0) {
			lineCurrent--;
			startPos = styler.LineStart(lineCurrent);
		}
	}
	int spaceFlags = 0;
	int indentCurrent = styler.IndentAmount(lineCurrent, &spaceFlags, IsComment);
	char chNext = styler[startPos];
	for (int i = startPos; i < endPos; i++) {
		char ch = chNext;
		chNext = styler.SafeGetCharAt(i + 1);

		if ((ch == '\r' && chNext != '\n') || (ch == '\n') || (i == endPos)) {
			int lev = indentCurrent;
			int indentNext = styler.IndentAmount(lineCurrent + 1, &spaceFlags, IsComment);
			if (!(indentCurrent & SC_FOLDLEVELWHITEFLAG)) {
				// Only non whitespace lines can be headers
				if ((indentCurrent & SC_FOLDLEVELNUMBERMASK) < (indentNext & SC_FOLDLEVELNUMBERMASK)) {
					lev |= SC_FOLDLEVELHEADERFLAG;
				} else if (indentNext & SC_FOLDLEVELWHITEFLAG) {
					// Line after is blank so check the next - maybe should continue further?
					int spaceFlags2 = 0;
					int indentNext2 = styler.IndentAmount(lineCurrent + 2, &spaceFlags2, IsComment);
					if ((indentCurrent & SC_FOLDLEVELNUMBERMASK) < (indentNext2 & SC_FOLDLEVELNUMBERMASK)) {
						lev |= SC_FOLDLEVELHEADERFLAG;
					}
				}
			}
			indentCurrent = indentNext;
			styler.SetLevel(lineCurrent, lev);
			lineCurrent++;
		}
	}
}

static void FoldMatlabDoc(unsigned int startPos, int length, int initStyle,
                          WordList *keywordlists[], Accessor &styler) {
  FoldMatlabOctaveDoc(startPos, length, initStyle, keywordlists, styler, IsMatlabComment);
}

static void FoldOctaveDoc(unsigned int startPos, int length, int initStyle,
                          WordList *keywordlists[], Accessor &styler) {
  FoldMatlabOctaveDoc(startPos, length, initStyle, keywordlists, styler, IsOctaveComment);
}

static const char * const matlabWordListDesc[] = {
	"Keywords",
	0
};

static const char * const octaveWordListDesc[] = {
	"Keywords",
	0
};

LexerModule lmMatlab(SCLEX_MATLAB, ColouriseMatlabDoc, "matlab", FoldMatlabDoc, matlabWordListDesc);

LexerModule lmOctave(SCLEX_OCTAVE, ColouriseOctaveDoc, "octave", FoldOctaveDoc, octaveWordListDesc);
<commit_msg>Patch from Bob M to handle \\ at end of string.<commit_after>// Scintilla source code edit control
/** @file LexMatlab.cxx
 ** Lexer for Matlab.
 ** Written by Jos Fonseca
 **
 ** Changes by Christoph Dalitz 2003/12/04:
 **   - added support for Octave
 **   - Strings can now be included both in single or double quotes
 **/
// Copyright 1998-2001 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.

#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <stdio.h>
#include <stdarg.h>

#include "Platform.h"

#include "PropSet.h"
#include "Accessor.h"
#include "StyleContext.h"
#include "KeyWords.h"
#include "Scintilla.h"
#include "SciLexer.h"

static bool IsMatlabCommentChar(int c) {
	return (c == '%') ;
}

static bool IsOctaveCommentChar(int c) {
	return (c == '%' || c == '#') ;
}

static bool IsMatlabComment(Accessor &styler, int pos, int len) {
	return len > 0 && IsMatlabCommentChar(styler[pos]) ;
}

static bool IsOctaveComment(Accessor &styler, int pos, int len) {
	return len > 0 && IsOctaveCommentChar(styler[pos]) ;
}

static inline bool IsAWordChar(const int ch) {
	return (ch < 0x80) && (isalnum(ch) || ch == '_');
}

static inline bool IsAWordStart(const int ch) {
	return (ch < 0x80) && (isalnum(ch) || ch == '_');
}

static void ColouriseMatlabOctaveDoc(
            unsigned int startPos, int length, int initStyle,
            WordList *keywordlists[], Accessor &styler,
            bool (*IsCommentChar)(int)) {

	WordList &keywords = *keywordlists[0];

	styler.StartAt(startPos);

	bool transpose = false;

	StyleContext sc(startPos, length, initStyle, styler);

	for (; sc.More(); sc.Forward()) {

		if (sc.state == SCE_MATLAB_OPERATOR) {
			if (sc.chPrev == '.') {
				if (sc.ch == '*' || sc.ch == '/' || sc.ch == '\\' || sc.ch == '^') {
					sc.ForwardSetState(SCE_MATLAB_DEFAULT);
					transpose = false;
				} else if (sc.ch == '\'') {
					sc.ForwardSetState(SCE_MATLAB_DEFAULT);
					transpose = true;
				} else {
					sc.SetState(SCE_MATLAB_DEFAULT);
				}
			} else {
				sc.SetState(SCE_MATLAB_DEFAULT);
			}
		} else if (sc.state == SCE_MATLAB_KEYWORD) {
			if (!isalnum(sc.ch) && sc.ch != '_') {
				char s[100];
				sc.GetCurrentLowered(s, sizeof(s));
				if (keywords.InList(s)) {
					sc.SetState(SCE_MATLAB_DEFAULT);
					transpose = false;
				} else {
					sc.ChangeState(SCE_MATLAB_IDENTIFIER);
					sc.SetState(SCE_MATLAB_DEFAULT);
					transpose = true;
				}
			}
		} else if (sc.state == SCE_MATLAB_NUMBER) {
			if (!isdigit(sc.ch) && sc.ch != '.'
			        && !(sc.ch == 'e' || sc.ch == 'E')
			        && !((sc.ch == '+' || sc.ch == '-') && (sc.chPrev == 'e' || sc.chPrev == 'E'))) {
				sc.SetState(SCE_MATLAB_DEFAULT);
				transpose = true;
			}
		} else if (sc.state == SCE_MATLAB_STRING) {
			if (sc.ch == '\\') {
				if (sc.chNext == '\"' || sc.chNext == '\'' || sc.chNext == '\\') {
					sc.Forward();
				}
			} else if (sc.ch == '\'') {
				sc.ForwardSetState(SCE_MATLAB_DEFAULT);
			}
		} else if (sc.state == SCE_MATLAB_DOUBLEQUOTESTRING) {
			if (sc.ch == '\\') {
				if (sc.chNext == '\"' || sc.chNext == '\'' || sc.chNext == '\\') {
					sc.Forward();
				}
			} else if (sc.ch == '\"') {
				sc.ForwardSetState(SCE_MATLAB_DEFAULT);
			}
		} else if (sc.state == SCE_MATLAB_COMMENT || sc.state == SCE_MATLAB_COMMAND) {
			if (sc.atLineEnd) {
				sc.SetState(SCE_MATLAB_DEFAULT);
				transpose = false;
			}
		}

		if (sc.state == SCE_MATLAB_DEFAULT) {
			if (IsCommentChar(sc.ch)) {
				sc.SetState(SCE_MATLAB_COMMENT);
			} else if (sc.ch == '!') {
				sc.SetState(SCE_MATLAB_COMMAND);
			} else if (sc.ch == '\'') {
				if (transpose) {
					sc.SetState(SCE_MATLAB_OPERATOR);
				} else {
					sc.SetState(SCE_MATLAB_STRING);
				}
			} else if (sc.ch == '"') {
				sc.SetState(SCE_MATLAB_DOUBLEQUOTESTRING);
			} else if (isdigit(sc.ch) || (sc.ch == '.' && isdigit(sc.chNext))) {
				sc.SetState(SCE_MATLAB_NUMBER);
			} else if (isalpha(sc.ch)) {
				sc.SetState(SCE_MATLAB_KEYWORD);
			} else if (isoperator(static_cast<char>(sc.ch)) || sc.ch == '@' || sc.ch == '\\') {
				if (sc.ch == ')' || sc.ch == ']') {
					transpose = true;
				} else {
					transpose = false;
				}
				sc.SetState(SCE_MATLAB_OPERATOR);
			} else {
				transpose = false;
			}
		}
	}
	sc.Complete();
}

static void ColouriseMatlabDoc(unsigned int startPos, int length, int initStyle,
                               WordList *keywordlists[], Accessor &styler) {
	ColouriseMatlabOctaveDoc(startPos, length, initStyle, keywordlists, styler, IsMatlabCommentChar);
}

static void ColouriseOctaveDoc(unsigned int startPos, int length, int initStyle,
                               WordList *keywordlists[], Accessor &styler) {
	ColouriseMatlabOctaveDoc(startPos, length, initStyle, keywordlists, styler, IsOctaveCommentChar);
}

static void FoldMatlabOctaveDoc(unsigned int startPos, int length, int,
                                WordList *[], Accessor &styler,
                                bool (*IsComment)(Accessor&, int, int)) {

	int endPos = startPos + length;

	// Backtrack to previous line in case need to fix its fold status
	int lineCurrent = styler.GetLine(startPos);
	if (startPos > 0) {
		if (lineCurrent > 0) {
			lineCurrent--;
			startPos = styler.LineStart(lineCurrent);
		}
	}
	int spaceFlags = 0;
	int indentCurrent = styler.IndentAmount(lineCurrent, &spaceFlags, IsComment);
	char chNext = styler[startPos];
	for (int i = startPos; i < endPos; i++) {
		char ch = chNext;
		chNext = styler.SafeGetCharAt(i + 1);

		if ((ch == '\r' && chNext != '\n') || (ch == '\n') || (i == endPos)) {
			int lev = indentCurrent;
			int indentNext = styler.IndentAmount(lineCurrent + 1, &spaceFlags, IsComment);
			if (!(indentCurrent & SC_FOLDLEVELWHITEFLAG)) {
				// Only non whitespace lines can be headers
				if ((indentCurrent & SC_FOLDLEVELNUMBERMASK) < (indentNext & SC_FOLDLEVELNUMBERMASK)) {
					lev |= SC_FOLDLEVELHEADERFLAG;
				} else if (indentNext & SC_FOLDLEVELWHITEFLAG) {
					// Line after is blank so check the next - maybe should continue further?
					int spaceFlags2 = 0;
					int indentNext2 = styler.IndentAmount(lineCurrent + 2, &spaceFlags2, IsComment);
					if ((indentCurrent & SC_FOLDLEVELNUMBERMASK) < (indentNext2 & SC_FOLDLEVELNUMBERMASK)) {
						lev |= SC_FOLDLEVELHEADERFLAG;
					}
				}
			}
			indentCurrent = indentNext;
			styler.SetLevel(lineCurrent, lev);
			lineCurrent++;
		}
	}
}

static void FoldMatlabDoc(unsigned int startPos, int length, int initStyle,
                          WordList *keywordlists[], Accessor &styler) {
	FoldMatlabOctaveDoc(startPos, length, initStyle, keywordlists, styler, IsMatlabComment);
}

static void FoldOctaveDoc(unsigned int startPos, int length, int initStyle,
                          WordList *keywordlists[], Accessor &styler) {
	FoldMatlabOctaveDoc(startPos, length, initStyle, keywordlists, styler, IsOctaveComment);
}

static const char * const matlabWordListDesc[] = {
	"Keywords",
	0
};

static const char * const octaveWordListDesc[] = {
	"Keywords",
	0
};

LexerModule lmMatlab(SCLEX_MATLAB, ColouriseMatlabDoc, "matlab", FoldMatlabDoc, matlabWordListDesc);

LexerModule lmOctave(SCLEX_OCTAVE, ColouriseOctaveDoc, "octave", FoldOctaveDoc, octaveWordListDesc);
<|endoftext|>


<commit_before>#include "NodeScene.h"

#include "CuteNodeWidget.h"

#include <QDebug>
#include <QGraphicsItem>
#include <QGraphicsSceneMouseEvent>
#include <QPainter>

NodeScene::NodeScene(const QRectF& sceneRect)
    : QGraphicsScene(sceneRect, nullptr)
    , _draggedItem{nullptr}
    , _draggingMousePointerOffset{0.0f, 0.0f}
    , _gridSize{20, 20}
    , _gridSnapping{true}
{

}

NodeScene::~NodeScene()
{

}

void NodeScene::ToggleGridSnapping()
{
    _gridSnapping = !_gridSnapping;
}

void NodeScene::drawBackground(QPainter* painter, const QRectF& rect)
{
    qreal left = int(rect.left()) - (int(rect.left()) % _gridSize.width());
    qreal top = int(rect.top()) - (int(rect.top()) % _gridSize.height());

    QVarLengthArray<QLineF, 100> lines;

    for (qreal x = left; x < rect.right(); x += _gridSize.width())
    {
        lines.append(QLineF(x, rect.top(), x, rect.bottom()));
    }

    for (qreal y = top; y < rect.bottom(); y += _gridSize.height())
    {
        lines.append(QLineF(rect.left(), y, rect.right(), y));
    }

    QPen pen(Qt::lightGray);
    painter->setPen(pen);
    painter->drawLines(lines.data(), lines.size());
}

void NodeScene::mouseMoveEvent(QGraphicsSceneMouseEvent* event)
{
    if (event->buttons() == Qt::LeftButton)
    {
        if (_draggedItem)
        {
            // Ensure that the item's offset from the mouse cursor stays the same.
            QPointF scenePos = event->scenePos() - _draggingMousePointerOffset;

            if (_gridSnapping)
            {
                scenePos.setX((round(scenePos.x() / _gridSize.width())) * _gridSize.width());
                scenePos.setY((round(scenePos.y() / _gridSize.height())) * _gridSize.height());
            }

            _draggedItem->setPos(scenePos);

            event->accept();
            return;
        }
    }

    event->ignore();
}

void NodeScene::mousePressEvent(QGraphicsSceneMouseEvent* event)
{
    if (event->buttons() == Qt::LeftButton)
    {
        QGraphicsItem* lowestItem = GetLowestItemThatWasClicked(event->scenePos());
        // we only want to enter dragging state when the lowest item is of type CuteNodeWidget
        _draggedItem = qgraphicsitem_cast<CuteNodeWidget*>(lowestItem);

        if (_draggedItem)
        {
            _draggingMousePointerOffset = event->scenePos() - _draggedItem->pos();

            // this removes glitches when moving an item after scrolling
            invalidate(_draggedItem->sceneBoundingRect());

            event->accept();
            return;
        }
    }

    event->ignore();
}

void NodeScene::mouseReleaseEvent(QGraphicsSceneMouseEvent* event)
{
    if (event->button() == Qt::LeftButton)
    {
        if (_draggedItem)
        {
            _draggedItem = nullptr;

            event->accept();
            return;
        }
    }

    event->ignore();
}

void NodeScene::wheelEvent(QGraphicsSceneWheelEvent* event)
{
    if (_draggedItem)
    {
        // this removes glitches when moving an item while scrolling
        invalidate(_draggedItem->sceneBoundingRect());
    }
    QGraphicsScene::wheelEvent(event);
}

QGraphicsItem* NodeScene::GetLowestItemThatWasClicked(const QPointF& clickPos)
{
    QList<QGraphicsItem*> clickedItems = items(clickPos, Qt::IntersectsItemShape, Qt::AscendingOrder);
    if (clickedItems.size() == 0)
    {
        return nullptr;
    }

    return clickedItems[0];
}
<commit_msg>Missing comment<commit_after>#include "NodeScene.h"

#include "CuteNodeWidget.h"

#include <QDebug>
#include <QGraphicsItem>
#include <QGraphicsSceneMouseEvent>
#include <QPainter>

NodeScene::NodeScene(const QRectF& sceneRect)
    : QGraphicsScene(sceneRect, nullptr)
    , _draggedItem{nullptr}
    , _draggingMousePointerOffset{0.0f, 0.0f}
    , _gridSize{20, 20}
    , _gridSnapping{true}
{

}

NodeScene::~NodeScene()
{

}

void NodeScene::ToggleGridSnapping()
{
    _gridSnapping = !_gridSnapping;
}

void NodeScene::drawBackground(QPainter* painter, const QRectF& rect)
{
    qreal left = int(rect.left()) - (int(rect.left()) % _gridSize.width());
    qreal top = int(rect.top()) - (int(rect.top()) % _gridSize.height());

    QVarLengthArray<QLineF, 100> lines;

    for (qreal x = left; x < rect.right(); x += _gridSize.width())
    {
        lines.append(QLineF(x, rect.top(), x, rect.bottom()));
    }

    for (qreal y = top; y < rect.bottom(); y += _gridSize.height())
    {
        lines.append(QLineF(rect.left(), y, rect.right(), y));
    }

    QPen pen(Qt::lightGray);
    painter->setPen(pen);
    painter->drawLines(lines.data(), lines.size());
}

void NodeScene::mouseMoveEvent(QGraphicsSceneMouseEvent* event)
{
    if (event->buttons() == Qt::LeftButton)
    {
        if (_draggedItem)
        {
            // Ensure that the item's offset from the mouse cursor stays the same.
            QPointF scenePos = event->scenePos() - _draggingMousePointerOffset;

            if (_gridSnapping)
            {
                scenePos.setX((round(scenePos.x() / _gridSize.width())) * _gridSize.width());
                scenePos.setY((round(scenePos.y() / _gridSize.height())) * _gridSize.height());
            }

            _draggedItem->setPos(scenePos);

            event->accept();
            return;
        }
    }

    event->ignore();
}

void NodeScene::mousePressEvent(QGraphicsSceneMouseEvent* event)
{
    if (event->buttons() == Qt::LeftButton)
    {
        QGraphicsItem* lowestItem = GetLowestItemThatWasClicked(event->scenePos());
        // we only want to enter dragging state when the lowest item is of type CuteNodeWidget
        _draggedItem = qgraphicsitem_cast<CuteNodeWidget*>(lowestItem);

        if (_draggedItem)
        {
            _draggingMousePointerOffset = event->scenePos() - _draggedItem->pos();

            // this removes glitches when moving an item after scrolling
            invalidate(_draggedItem->sceneBoundingRect());

            event->accept();
            return;
        }
    }

    event->ignore();
}

void NodeScene::mouseReleaseEvent(QGraphicsSceneMouseEvent* event)
{
    // for some reason buttons() will show Qt::NoButton, so we call button() instead
    if (event->button() == Qt::LeftButton)
    {
        if (_draggedItem)
        {
            _draggedItem = nullptr;

            event->accept();
            return;
        }
    }

    event->ignore();
}

void NodeScene::wheelEvent(QGraphicsSceneWheelEvent* event)
{
    if (_draggedItem)
    {
        // this removes glitches when moving an item while scrolling
        invalidate(_draggedItem->sceneBoundingRect());
    }
    QGraphicsScene::wheelEvent(event);
}

QGraphicsItem* NodeScene::GetLowestItemThatWasClicked(const QPointF& clickPos)
{
    QList<QGraphicsItem*> clickedItems = items(clickPos, Qt::IntersectsItemShape, Qt::AscendingOrder);
    if (clickedItems.size() == 0)
    {
        return nullptr;
    }

    return clickedItems[0];
}
<|endoftext|>