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

         Yuri V. Krugloff. 2013-2015. http://www.tver-soft.org

    This is free and unencumbered software released into the public domain.

    Anyone is free to copy, modify, publish, use, compile, sell, or
    distribute this software, either in source code form or as a compiled
    binary, for any purpose, commercial or non-commercial, and by any
    means.

    In jurisdictions that recognize copyright laws, the author or authors
    of this software dedicate any and all copyright interest in the
    software to the public domain. We make this dedication for the benefit
    of the public at large and to the detriment of our heirs and
    successors. We intend this dedication to be an overt act of
    relinquishment in perpetuity of all present and future rights to this
    software under copyright law.

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

    For more information, please refer to <http://unlicense.org/>

*******************************************************************************/

#include "Functions.hpp"

#include <stdlib.h>
#include <string.h>
#include <errno.h>
#if defined(OS_WINDOWS)
#include <io.h>
#include <direct.h>
#elif defined(OS_LINUX) || defined(OS_MACOS)
#include <sys/stat.h>
#include <unistd.h>
#include <glob.h>
#include <limits.h>
#endif
#include <time.h>

#include "Logger.hpp"

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

using namespace std;

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

#ifdef _MSC_VER
#define GETCWD _getcwd
#define POPEN  _popen
#define PCLOSE _pclose
#else
#define GETCWD getcwd
#define POPEN  popen
#define PCLOSE pclose
#endif

#ifdef OS_WINDOWS
#define POPEN_MODE "rt"
#else
#define POPEN_MODE "r"
#endif

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

void eraseLastSeparators(string* pStr)
{
    for (int i = static_cast<int>(pStr->size()) - 1; i >= 0; --i) {
        const char& c = pStr->at(i);
        if (c == '/' || c == '\\')
            pStr->erase(i);
        else
            break;
    }
}

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

void addLastSeparator(string* pStr)
{
    if (pStr != NULL && !pStr->empty()) {
        string::const_reverse_iterator Iter = pStr->rbegin();
        if (*Iter != '/' && *Iter != '\\')
            *pStr += Functions::separator();
    }
}

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

void Functions::splice(TStringList* pX, TStringList Y)
{
    if (pX != NULL && !Y.empty()) {
        TStringList::iterator Iter = pX->end();
        pX->splice(Iter, Y);
    }
}

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

char Functions::separator()
{
    return '/';
}

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

string Functions::normalizeSeparators(const string &path)
{
#ifdef OS_WINDOWS
    string Result = path;
    for (string::iterator Iter = Result.begin(); Iter != Result.end(); ++Iter)
        if (*Iter == '\\')
            *Iter = '/';
    return Result;
#else
    return path;
#endif
}

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

string Functions::trimSeparators(const string& str)
{
    string Result;
    string::size_type first = 0;
    for (; first < str.length(); ++first)
        if (str[first] != '/' && str[first] != '\\')
            break;
    Result = str.substr(first);
    eraseLastSeparators(&Result);
    return Result;
}

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

bool Functions::hasOnlyNormalSeparators(const char* path)
{
    while (*path != '\0')
        if (*path++ == '\\')
            return false;
    return true;
}

//------------------------------------------------------------------------------
// Replace char in string to new substring.

void Functions::replace(string* pS, char before, const char* after)
{
    size_t len = strlen(after);
    size_t pos = pS->find(before);
    while (pos != string::npos) {
        pS->replace(pos, 1, after);
        pos = pS->find(before, pos + len);
    }
}

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

bool Functions::startsWith(const string& str, const char* start)
{
    return str.compare(0, strlen(start), start) == 0;
}

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

string Functions::absolutePath(const string& relativePath)
{
    string Result;
#if defined(OS_WINDOWS)
    char AbsPath[_MAX_PATH];
    if (_fullpath(AbsPath, relativePath.c_str(), _MAX_PATH) != NULL)
        Result = normalizeSeparators(AbsPath);
#elif defined(OS_LINUX) || defined(OS_MACOS)
    char AbsPath[PATH_MAX];
    if (realpath(relativePath.c_str(), AbsPath) != NULL)
        Result = AbsPath;
#else
#error "Unsupported OS."
#endif

    if (Result.empty()) {
        LOG_E("Error translate path to absolute. Error %i.\n"
            "  (%s)",
            errno, relativePath.c_str());
    }

    eraseLastSeparators(&Result);
    return Result;
}

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

string Functions::currentDir()
{
    string Result;

    char* cwd = GETCWD(NULL, 0);
    if (cwd != NULL) {
        Result = cwd;
        free(cwd);
    } else {
        LOG_E("Error getting current directory. Error %i.\n", errno);
    }

    eraseLastSeparators(&Result);

    return normalizeSeparators(Result);
}

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

bool Functions::isFileExists(const char* fileName)
{
#if defined(OS_WINDOWS)
    _finddata_t FindData;
    intptr_t FindHandle = _findfirst(fileName, &FindData);
    if (FindHandle != -1) {
        _findclose(FindHandle);
        return true;
    }
#elif defined(OS_LINUX) || defined(OS_MACOS)
    glob_t GlobData;
    if (glob(fileName, 0, NULL, &GlobData) == 0) {
        globfree(&GlobData);
        return true;
    }
#else
#error "Unsupported OS."
#endif

    return false;
}

//------------------------------------------------------------------------------
// Getting size of opened file.

long Functions::getFileSize(FILE* file)
{
#if defined(OS_WINDOWS)
    return _filelength(_fileno(file));
#elif defined(OS_LINUX) || defined(OS_MACOS)
    struct stat Stat;
    if (fstat(fileno(file), &Stat) == 0)
        return Stat.st_size;
    return -1;
#else
#error "Unsupported OS."
#endif
}

//------------------------------------------------------------------------------
// Truncation file to empty (zero size).

bool Functions::zeroFile(FILE* file)
{
    rewind(file);
#if defined(OS_WINDOWS)
    return _chsize(_fileno(file), 0) == 0;
#elif defined(OS_LINUX) || defined(OS_MACOS)
    return ftruncate(fileno(file), 0) == 0;
#else
#error "Unsupported OS."
#endif
}
//------------------------------------------------------------------------------

bool Functions::renameFile(const char* oldFileName, const char* newFileName)
{
    LOG_V("Renaming file \"%s\"\n"
        "           to \"%s\".\n",
        oldFileName, newFileName);

    if (!rename(oldFileName, newFileName) == 0) {
        LOG_E("Error renaming file \"%s\" to \"%s\". Error %i.\n",
            oldFileName, newFileName, errno);
        return false;
    }
    return true;
}

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

bool Functions::copyFile(const char* fromFileName, const char* toFileName)
{
    LOG_V("Copying file content from \"%s\"\n"
        "                       to \"%s\".\n",
        fromFileName, toFileName);

    bool Result = true;
    FILE* src = fopen(fromFileName, "rb");
    if (src != NULL) {
        FILE* dst = fopen(toFileName, "wb");
        if (dst != NULL) {
            char Buffer[1024 * 32];  // 32kb
            while (!feof(src)) {
                size_t size = fread(Buffer, 1, sizeof(Buffer), src);
                if (fwrite(Buffer, 1, size, dst) != size) {
                    LOG_E("Error writing to file \"%s\".\n", toFileName);
                    Result = false;
                    break;
                }
            }
            fclose(dst);
            if (!Result)
                removeFile(toFileName);
        } else {
            LOG_E("Error opening file \"%s\" for writing.\n", toFileName);
            Result = false;
        }
        fclose(src);
    } else {
        LOG_E("Error opening file \"%s\" for reading.\n", fromFileName);
        Result = false;
    }

    return Result;
}

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

bool Functions::removeFile(const char* fileName)
{
    if (isFileExists(fileName)) {
        LOG_V("Removing file \"%s\"...\n", fileName);
        if (remove(fileName) != 0) {
            LOG_E("Error removing file \"%s\". Error %i.\n", fileName, errno);
            return false;
        }
    }
    return true;
}

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

string Functions::currentTime(const char* format)
{
    time_t rawTime = time(NULL);
    struct tm* timeInfo = localtime(&rawTime);
    const int BufferSize = 1024;
    char Buffer[BufferSize];
    strftime(Buffer, BufferSize, format, timeInfo);
    return Buffer;
}

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

std::string Functions::getProgramOutput(const char* fileName)
{
    string Result;
    FILE* out;
    out = POPEN(fileName, POPEN_MODE);
    if (out != NULL) {
        char Buffer[1024];
        while (fgets(Buffer, sizeof(Buffer), out) != NULL)
            Result += Buffer;
        if (ferror(out) != 0) {
            LOG_E("Error reading from pipe. Error %i.\n", errno);
            Result.clear();
        }
        if (PCLOSE(out) == -1)
            LOG_E("Error closing pipe. Error %i.\n", errno);
    } else {
        LOG_E("Error running program \"%s\". Error %i.\n", fileName, errno);
    }

    return Result;
}

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

TStringList Functions::findFiles(string dir, const string& mask)
{
    TStringList Result;

    addLastSeparator(&dir);

#if defined(OS_WINDOWS)
    _finddata_t FindData;
    intptr_t FindHandle = _findfirst((dir + mask).c_str(), &FindData);
    if (FindHandle != -1) {
        do {
            if ((FindData.attrib & _A_SUBDIR) == 0)
                Result.push_back(dir + FindData.name);
        } while (_findnext(FindHandle, &FindData) == 0);
        _findclose(FindHandle);
    }
#elif defined(OS_LINUX) || defined(OS_MACOS)
    glob_t GlobData;
    if (glob((dir + mask).c_str(), GLOB_MARK, NULL, &GlobData) == 0) {
        for (size_t i = 0; i < GlobData.gl_pathc; ++i) {
            const char* path = GlobData.gl_pathv[i];
            if (path[strlen(path) - 1] != '/')
                Result.push_back(path);
        }
        globfree(&GlobData);
    }
#else
#error "Unsupported OS."
#endif

    return Result;
}

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

TStringList Functions::findFilesRecursive(string dir, const string& mask)
{
    TStringList Result;

    addLastSeparator(&dir);

#if defined (OS_WINDOWS)
    _finddata_t FindData;
    intptr_t FindHandle = _findfirst((dir + "*").c_str(), &FindData);
    if (FindHandle != -1) {
        do {
            if ((FindData.attrib & _A_SUBDIR) != 0 &&
                strcmp(FindData.name, ".") != 0 &&
                strcmp(FindData.name, "..") != 0) {
                splice(&Result, findFilesRecursive(dir + FindData.name + "/", mask));
            }
        } while (_findnext(FindHandle, &FindData) == 0);
        _findclose(FindHandle);
    }
    splice(&Result, findFiles(dir, mask));
#elif defined (OS_LINUX) || defined(OS_MACOS)
    glob_t GlobData;
    if (glob((dir + mask).c_str(), GLOB_MARK, NULL, &GlobData) == 0) {
        for (size_t i = 0; i < GlobData.gl_pathc; ++i) {
            const char* path = GlobData.gl_pathv[i];
            if (path[strlen(path) - 1] != '/')
                Result.push_back(path);
            else
                splice(&Result, findFilesRecursive(path, mask));
        }
    }
#endif

    return Result;
}

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

string Functions::stringListToStr(const TStringList& list, const string& prefix, const string& suffix)
{
    string Result;
    for (TStringList::const_iterator Iter = list.begin(); Iter != list.end(); ++Iter)
        Result += prefix + *Iter + suffix;
    return Result;
}

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

string Functions::stringMapToStr(const TStringMap& map, const string& prefix, const string& separator, const string& suffix)
{
    string Result;
    for (TStringMap::const_iterator Iter = map.begin(); Iter != map.end(); ++Iter)
        Result += prefix + Iter->first + separator + Iter->second + suffix;
    return Result;
}

//------------------------------------------------------------------------------
<commit_msg>Silent warnings reported by Clang<commit_after>/*******************************************************************************

         Yuri V. Krugloff. 2013-2015. http://www.tver-soft.org

    This is free and unencumbered software released into the public domain.

    Anyone is free to copy, modify, publish, use, compile, sell, or
    distribute this software, either in source code form or as a compiled
    binary, for any purpose, commercial or non-commercial, and by any
    means.

    In jurisdictions that recognize copyright laws, the author or authors
    of this software dedicate any and all copyright interest in the
    software to the public domain. We make this dedication for the benefit
    of the public at large and to the detriment of our heirs and
    successors. We intend this dedication to be an overt act of
    relinquishment in perpetuity of all present and future rights to this
    software under copyright law.

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

    For more information, please refer to <http://unlicense.org/>

*******************************************************************************/

#include "Functions.hpp"

#include <stdlib.h>
#include <string.h>
#include <errno.h>
#if defined(OS_WINDOWS)
#include <io.h>
#include <direct.h>
#elif defined(OS_LINUX) || defined(OS_MACOS)
#include <sys/stat.h>
#include <unistd.h>
#include <glob.h>
#include <limits.h>
#endif
#include <time.h>

#include "Logger.hpp"

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

using namespace std;

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

#ifdef _MSC_VER
#define GETCWD _getcwd
#define POPEN  _popen
#define PCLOSE _pclose
#else
#define GETCWD getcwd
#define POPEN  popen
#define PCLOSE pclose
#endif

#ifdef OS_WINDOWS
#define POPEN_MODE "rt"
#else
#define POPEN_MODE "r"
#endif

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

void eraseLastSeparators(string* pStr)
{
    for (int i = static_cast<int>(pStr->size()) - 1; i >= 0; --i) {
        const char& c = pStr->at(i);
        if (c == '/' || c == '\\')
            pStr->erase(i);
        else
            break;
    }
}

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

void addLastSeparator(string* pStr)
{
    if (pStr != NULL && !pStr->empty()) {
        string::const_reverse_iterator Iter = pStr->rbegin();
        if (*Iter != '/' && *Iter != '\\')
            *pStr += Functions::separator();
    }
}

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

void Functions::splice(TStringList* pX, TStringList Y)
{
    if (pX != NULL && !Y.empty()) {
        TStringList::iterator Iter = pX->end();
        pX->splice(Iter, Y);
    }
}

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

char Functions::separator()
{
    return '/';
}

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

string Functions::normalizeSeparators(const string &path)
{
#ifdef OS_WINDOWS
    string Result = path;
    for (string::iterator Iter = Result.begin(); Iter != Result.end(); ++Iter)
        if (*Iter == '\\')
            *Iter = '/';
    return Result;
#else
    return path;
#endif
}

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

string Functions::trimSeparators(const string& str)
{
    string Result;
    string::size_type first = 0;
    for (; first < str.length(); ++first)
        if (str[first] != '/' && str[first] != '\\')
            break;
    Result = str.substr(first);
    eraseLastSeparators(&Result);
    return Result;
}

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

bool Functions::hasOnlyNormalSeparators(const char* path)
{
    while (*path != '\0')
        if (*path++ == '\\')
            return false;
    return true;
}

//------------------------------------------------------------------------------
// Replace char in string to new substring.

void Functions::replace(string* pS, char before, const char* after)
{
    size_t len = strlen(after);
    size_t pos = pS->find(before);
    while (pos != string::npos) {
        pS->replace(pos, 1, after);
        pos = pS->find(before, pos + len);
    }
}

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

bool Functions::startsWith(const string& str, const char* start)
{
    return str.compare(0, strlen(start), start) == 0;
}

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

string Functions::absolutePath(const string& relativePath)
{
    string Result;
#if defined(OS_WINDOWS)
    char AbsPath[_MAX_PATH];
    if (_fullpath(AbsPath, relativePath.c_str(), _MAX_PATH) != NULL)
        Result = normalizeSeparators(AbsPath);
#elif defined(OS_LINUX) || defined(OS_MACOS)
    char AbsPath[PATH_MAX];
    if (realpath(relativePath.c_str(), AbsPath) != NULL)
        Result = AbsPath;
#else
#error "Unsupported OS."
#endif

    if (Result.empty()) {
        LOG_E("Error translate path to absolute. Error %i.\n"
            "  (%s)",
            errno, relativePath.c_str());
    }

    eraseLastSeparators(&Result);
    return Result;
}

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

string Functions::currentDir()
{
    string Result;

    char* cwd = GETCWD(NULL, 0);
    if (cwd != NULL) {
        Result = cwd;
        free(cwd);
    } else {
        LOG_E("Error getting current directory. Error %i.\n", errno);
    }

    eraseLastSeparators(&Result);

    return normalizeSeparators(Result);
}

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

bool Functions::isFileExists(const char* fileName)
{
#if defined(OS_WINDOWS)
    _finddata_t FindData;
    intptr_t FindHandle = _findfirst(fileName, &FindData);
    if (FindHandle != -1) {
        _findclose(FindHandle);
        return true;
    }
#elif defined(OS_LINUX) || defined(OS_MACOS)
    glob_t GlobData;
    if (glob(fileName, 0, NULL, &GlobData) == 0) {
        globfree(&GlobData);
        return true;
    }
#else
#error "Unsupported OS."
#endif

    return false;
}

//------------------------------------------------------------------------------
// Getting size of opened file.

long Functions::getFileSize(FILE* file)
{
#if defined(OS_WINDOWS)
    return _filelength(_fileno(file));
#elif defined(OS_LINUX) || defined(OS_MACOS)
    struct stat Stat;
    if (fstat(fileno(file), &Stat) == 0)
        return Stat.st_size;
    return -1;
#else
#error "Unsupported OS."
#endif
}

//------------------------------------------------------------------------------
// Truncation file to empty (zero size).

bool Functions::zeroFile(FILE* file)
{
    rewind(file);
#if defined(OS_WINDOWS)
    return _chsize(_fileno(file), 0) == 0;
#elif defined(OS_LINUX) || defined(OS_MACOS)
    return ftruncate(fileno(file), 0) == 0;
#else
#error "Unsupported OS."
#endif
}
//------------------------------------------------------------------------------

bool Functions::renameFile(const char* oldFileName, const char* newFileName)
{
    LOG_V("Renaming file \"%s\"\n"
        "           to \"%s\".\n",
        oldFileName, newFileName);

    if (rename(oldFileName, newFileName) != 0) {
        LOG_E("Error renaming file \"%s\" to \"%s\". Error %i.\n",
            oldFileName, newFileName, errno);
        return false;
    }
    return true;
}

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

bool Functions::copyFile(const char* fromFileName, const char* toFileName)
{
    LOG_V("Copying file content from \"%s\"\n"
        "                       to \"%s\".\n",
        fromFileName, toFileName);

    bool Result = true;
    FILE* src = fopen(fromFileName, "rb");
    if (src != NULL) {
        FILE* dst = fopen(toFileName, "wb");
        if (dst != NULL) {
            char Buffer[1024 * 32];  // 32kb
            while (!feof(src)) {
                size_t size = fread(Buffer, 1, sizeof(Buffer), src);
                if (fwrite(Buffer, 1, size, dst) != size) {
                    LOG_E("Error writing to file \"%s\".\n", toFileName);
                    Result = false;
                    break;
                }
            }
            fclose(dst);
            if (!Result)
                removeFile(toFileName);
        } else {
            LOG_E("Error opening file \"%s\" for writing.\n", toFileName);
            Result = false;
        }
        fclose(src);
    } else {
        LOG_E("Error opening file \"%s\" for reading.\n", fromFileName);
        Result = false;
    }

    return Result;
}

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

bool Functions::removeFile(const char* fileName)
{
    if (isFileExists(fileName)) {
        LOG_V("Removing file \"%s\"...\n", fileName);
        if (remove(fileName) != 0) {
            LOG_E("Error removing file \"%s\". Error %i.\n", fileName, errno);
            return false;
        }
    }
    return true;
}

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

string Functions::currentTime(const char* format)
{
    time_t rawTime = time(NULL);
    struct tm* timeInfo = localtime(&rawTime);
    const int BufferSize = 1024;
    char Buffer[BufferSize];
    strftime(Buffer, BufferSize, format, timeInfo);
    return Buffer;
}

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

std::string Functions::getProgramOutput(const char* fileName)
{
    string Result;
    FILE* out;
    out = POPEN(fileName, POPEN_MODE);
    if (out != NULL) {
        char Buffer[1024];
        while (fgets(Buffer, sizeof(Buffer), out) != NULL)
            Result += Buffer;
        if (ferror(out) != 0) {
            LOG_E("Error reading from pipe. Error %i.\n", errno);
            Result.clear();
        }
        if (PCLOSE(out) == -1)
            LOG_E("Error closing pipe. Error %i.\n", errno);
    } else {
        LOG_E("Error running program \"%s\". Error %i.\n", fileName, errno);
    }

    return Result;
}

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

TStringList Functions::findFiles(string dir, const string& mask)
{
    TStringList Result;

    addLastSeparator(&dir);

#if defined(OS_WINDOWS)
    _finddata_t FindData;
    intptr_t FindHandle = _findfirst((dir + mask).c_str(), &FindData);
    if (FindHandle != -1) {
        do {
            if ((FindData.attrib & _A_SUBDIR) == 0)
                Result.push_back(dir + FindData.name);
        } while (_findnext(FindHandle, &FindData) == 0);
        _findclose(FindHandle);
    }
#elif defined(OS_LINUX) || defined(OS_MACOS)
    glob_t GlobData;
    if (glob((dir + mask).c_str(), GLOB_MARK, NULL, &GlobData) == 0) {
        for (size_t i = 0; i < GlobData.gl_pathc; ++i) {
            const char* path = GlobData.gl_pathv[i];
            if (path[strlen(path) - 1] != '/')
                Result.push_back(path);
        }
        globfree(&GlobData);
    }
#else
#error "Unsupported OS."
#endif

    return Result;
}

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

TStringList Functions::findFilesRecursive(string dir, const string& mask)
{
    TStringList Result;

    addLastSeparator(&dir);

#if defined (OS_WINDOWS)
    _finddata_t FindData;
    intptr_t FindHandle = _findfirst((dir + "*").c_str(), &FindData);
    if (FindHandle != -1) {
        do {
            if ((FindData.attrib & _A_SUBDIR) != 0 &&
                strcmp(FindData.name, ".") != 0 &&
                strcmp(FindData.name, "..") != 0) {
                splice(&Result, findFilesRecursive(dir + FindData.name + "/", mask));
            }
        } while (_findnext(FindHandle, &FindData) == 0);
        _findclose(FindHandle);
    }
    splice(&Result, findFiles(dir, mask));
#elif defined (OS_LINUX) || defined(OS_MACOS)
    glob_t GlobData;
    if (glob((dir + mask).c_str(), GLOB_MARK, NULL, &GlobData) == 0) {
        for (size_t i = 0; i < GlobData.gl_pathc; ++i) {
            const char* path = GlobData.gl_pathv[i];
            if (path[strlen(path) - 1] != '/')
                Result.push_back(path);
            else
                splice(&Result, findFilesRecursive(path, mask));
        }
    }
#endif

    return Result;
}

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

string Functions::stringListToStr(const TStringList& list, const string& prefix, const string& suffix)
{
    string Result;
    for (TStringList::const_iterator Iter = list.begin(); Iter != list.end(); ++Iter)
        Result += prefix + *Iter + suffix;
    return Result;
}

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

string Functions::stringMapToStr(const TStringMap& map, const string& prefix, const string& separator, const string& suffix)
{
    string Result;
    for (TStringMap::const_iterator Iter = map.begin(); Iter != map.end(); ++Iter)
        Result += prefix + Iter->first + separator + Iter->second + suffix;
    return Result;
}

//------------------------------------------------------------------------------
<|endoftext|>


<commit_before>#include "NativeExample.h"
#include <iostream>

using namespace std;

JNIEXPORT void JNICALL Java_NativeExample_callMe
(JNIEnv *env, jobject jobj){
  std::cout<<"Hello .."<<std::endl;
  jclass classObject = env->FindClass("Abc");
  if(classObject==0){
    std::cout<<"couldn't find class"<<std::endl;
  }else{
    std::cout<<"found class"<<std::endl;
  }

  // use javap -s Abc to find methog signatures
  jmethodID constructor = env->GetMethodID(classObject,"<init>","()V");
  jobject object = env->NewObject(classObject, constructor);

  // use javap -s Abc to find methog signatures
  jmethodID method1Object = env->GetMethodID(classObject,"method1","(I)V");
  if(method1Object==0){
    std::cout<<"coudn't find method"<<std::endl;
  }else{
    std::cout<<"found method"<<std::endl;
  }
  object = env->NewObject(classObject, method1Object,2);

}

JNIEXPORT void JNICALL Java_NativeExample_printf
(JNIEnv *env, jobject job, jstring jstring){
  const char *nativeString = env->GetStringUTFChars(jstring,0);
  std::cout<<nativeString<<std::endl;
  env->ReleaseStringUTFChars(jstring, nativeString);
}
<commit_msg>method calls comments<commit_after>#include "NativeExample.h"
#include <iostream>

using namespace std;

JNIEXPORT void JNICALL Java_NativeExample_callMe
(JNIEnv *env, jobject jobj){
  std::cout<<"Hello .."<<std::endl;
  jclass classObject = env->FindClass("Abc");
  if(classObject==0){
    std::cout<<"couldn't find class"<<std::endl;
  }else{
    std::cout<<"found class"<<std::endl;
  }

  // use javap -s Abc to find method signatures
  jmethodID constructor = env->GetMethodID(classObject,"<init>","()V");
  jobject object = env->NewObject(classObject, constructor);

  // use javap -s Abc to find method signatures
  jmethodID method1Object = env->GetMethodID(classObject,"method1","(I)V");
  if(method1Object==0){
    std::cout<<"coudn't find method"<<std::endl;
  }else{
    std::cout<<"found method"<<std::endl;
  }
  object = env->NewObject(classObject, method1Object,2);

}

JNIEXPORT void JNICALL Java_NativeExample_printf
(JNIEnv *env, jobject job, jstring jstring){
  const char *nativeString = env->GetStringUTFChars(jstring,0);
  std::cout<<nativeString<<std::endl;
  env->ReleaseStringUTFChars(jstring, nativeString);
}
<|endoftext|>


<commit_before>/*
 * This file is part of Playslave-C++.
 * Playslave-C++ is licenced under MIT License. See LICENSE.txt for more
 * details.
 */

#define _POSIX_C_SOURCE 200809

#include <memory>
#include <sstream>
#include <vector>

#include <cstdarg>  /* CurrentStateIn */
#include <cstdbool> /* bool */
#include <cstdint>
#include <cstdlib>
#include <cstring>

#include <algorithm>
#include <thread>
#include <chrono>

#ifdef WIN32
struct timespec {
	time_t tv_sec;
	long tv_nsec;
};
#include <Winsock2.h>
#else
#include <time.h> /* struct timespec */
#endif

#include "cmd.h" /* struct cmd, check_commands */
#include "io.hpp"

#include "audio.h"
#include "constants.h"
#include "messages.h"
#include "player.h"

Player::Player(const std::string &device) : device(device)
{
	this->current_state = State::EJECTED;
	this->au = nullptr;

	this->position_listener = nullptr;
	this->position_period = std::chrono::microseconds(0);
	this->position_last = std::chrono::microseconds(0);
	this->position_last_invalid = true;
}

bool Player::Eject()
{
	bool valid = CurrentStateIn({State::STOPPED, State::PLAYING});
	if (valid) {
		this->au = nullptr;
		SetState(State::EJECTED);
		this->position_last = std::chrono::microseconds(0);
	}
	return valid;
}

bool Player::Play()
{
	bool valid = CurrentStateIn({State::STOPPED}) && (this->au != nullptr);
	if (valid) {
		this->au->Start();
		SetState(State::PLAYING);
	}
	return valid;
}

bool Player::Quit()
{
	Eject();
	SetState(State::QUITTING);

	return true; // Always a valid command.
}

bool Player::Stop()
{
	bool valid = CurrentStateIn({State::PLAYING});
	if (valid) {
		this->au->Stop();
		SetState(State::STOPPED);
	}
	return valid;
}

bool Player::Load(const std::string &filename)
{
	try
	{
		this->au = std::unique_ptr<AudioOutput>(
		                new AudioOutput(filename, this->device));
		this->position_last_invalid = true;
		Debug("Loaded ", filename);
		SetState(State::STOPPED);
	}
	catch (Error &error)
	{
		error.ToResponse();
		Eject();
	}

	return true; // Always a valid command.
}

bool Player::Seek(const std::string &time_str)
{
	/* TODO: proper overflow checking */

	std::istringstream is(time_str);
	uint64_t raw_time;
	std::string rest;
	is >> raw_time >> rest;

	std::chrono::microseconds position(0);
	if (rest == "s" || rest == "sec") {
		position = std::chrono::duration_cast<
		                std::chrono::microseconds>(
		                std::chrono::seconds(raw_time));

	} else {
		/* Assume microseconds */
		position = std::chrono::microseconds(raw_time);
	}

	/* Weed out any unwanted states */
	bool valid = CurrentStateIn({State::PLAYING, State::STOPPED});
	if (valid) {
		// enum state current_state = this->cstate;

		// cmd_stop(); // We need the player engine stopped in order to
		// seek
		this->au->SeekToPosition(position);
		this->position_last_invalid = true;

		// if (current_state == S_PLAY) {
		// If we were playing before we'd ideally like to resume
		// cmd_play();
		//}
	}

	return valid;
}

State Player::CurrentState()
{
	return this->current_state;
}

/* Performs an iteration of the player update loop. */
void Player::Update()
{
	if (this->current_state == State::PLAYING) {
		if (this->au->IsHalted()) {
			Eject();
		} else {
			SendPositionIfReady();
		}
	}
	if (CurrentStateIn({State::PLAYING, State::STOPPED})) {
		bool more = this->au->Update();
		if (!more) {
			Eject();
		}
	}
}

/* Throws an error if the current state is not in the state set provided by
 * the initializer_list.
 */
bool Player::CurrentStateIn(std::initializer_list<State> states)
{
	return std::any_of(states.begin(), states.end(), [this](State state) {
		return this->current_state == state;
	});
}

/* Sets the player state and honks accordingly. */
void Player::SetState(State state)
{
	State last_state = this->current_state;

	this->current_state = state;

	if (this->state_listener != nullptr) {
		this->state_listener(last_state, state);
	}
}

/**
 * Registers a listener for state changes.
 * @param listener The function to which state change signals shall be sent.
 */
void Player::RegisterStateListener(StateListener listener)
{
	this->state_listener = listener;
}

/**
 * Sends a position signal to the outside environment, if ready to send one.
 * This only sends a signal if the requested amount of time has passed since the
 * last one.
 * It requires a handler to have been registered via SetTimeSignalHandler.
 */
void Player::SendPositionIfReady()
{
	auto position = this->au->CurrentPosition<std::chrono::microseconds>();
	if (IsReadyToSendPosition(position)) {
		this->position_listener(position);
		this->position_last = position;
		this->position_last_invalid = false;
	}
}

/**
 * Figures out whether it's time to send a position signal.
 * @param current_position The current position in the song.
 * @return True if enough time has elapsed for a signal to be sent; false
 * otherwise.
 */
bool Player::IsReadyToSendPosition(std::chrono::microseconds current_position)
{
	bool ready = false;

	if (this->position_last_invalid) {
		ready = true;
	} else if (this->position_listener != nullptr) {
		auto difference = current_position - this->position_last;
		ready = difference >= this->position_period;
	}

	return ready;
}

/**
 * Registers a listener for position signals.
 * @param listener The function to which position signals shall be sent.
 * @param period_usecs The approximate period, in microseconds, between position
 * signals.
 */
void Player::RegisterPositionListener(PositionListener listener,
                                      const std::chrono::microseconds period)
{
	this->position_listener = listener;
	this->position_period = period;
}
<commit_msg>Don't attempt to load with an empty filename<commit_after>/*
 * This file is part of Playslave-C++.
 * Playslave-C++ is licenced under MIT License. See LICENSE.txt for more
 * details.
 */

#define _POSIX_C_SOURCE 200809

#include <memory>
#include <sstream>
#include <vector>

#include <cstdarg>  /* CurrentStateIn */
#include <cstdbool> /* bool */
#include <cstdint>
#include <cstdlib>
#include <cstring>

#include <algorithm>
#include <thread>
#include <chrono>

#ifdef WIN32
struct timespec {
	time_t tv_sec;
	long tv_nsec;
};
#include <Winsock2.h>
#else
#include <time.h> /* struct timespec */
#endif

#include "cmd.h" /* struct cmd, check_commands */
#include "io.hpp"

#include "audio.h"
#include "constants.h"
#include "messages.h"
#include "player.h"

Player::Player(const std::string &device) : device(device)
{
	this->current_state = State::EJECTED;
	this->au = nullptr;

	this->position_listener = nullptr;
	this->position_period = std::chrono::microseconds(0);
	this->position_last = std::chrono::microseconds(0);
	this->position_last_invalid = true;
}

bool Player::Eject()
{
	bool valid = CurrentStateIn({State::STOPPED, State::PLAYING});
	if (valid) {
		this->au = nullptr;
		SetState(State::EJECTED);
		this->position_last = std::chrono::microseconds(0);
	}
	return valid;
}

bool Player::Play()
{
	bool valid = CurrentStateIn({State::STOPPED}) && (this->au != nullptr);
	if (valid) {
		this->au->Start();
		SetState(State::PLAYING);
	}
	return valid;
}

bool Player::Quit()
{
	Eject();
	SetState(State::QUITTING);

	return true; // Always a valid command.
}

bool Player::Stop()
{
	bool valid = CurrentStateIn({State::PLAYING});
	if (valid) {
		this->au->Stop();
		SetState(State::STOPPED);
	}
	return valid;
}

bool Player::Load(const std::string &filename)
{
	if (filename.length() == 0) return false;

	try
	{
		this->au = std::unique_ptr<AudioOutput>(
		                new AudioOutput(filename, this->device));
		this->position_last_invalid = true;
		Debug("Loaded ", filename);
		SetState(State::STOPPED);
	}
	catch (Error &error)
	{
		error.ToResponse();
		Eject();
	}

	return true; // Always a valid command.
}

bool Player::Seek(const std::string &time_str)
{
	/* TODO: proper overflow checking */

	std::istringstream is(time_str);
	uint64_t raw_time;
	std::string rest;
	is >> raw_time >> rest;

	std::chrono::microseconds position(0);
	if (rest == "s" || rest == "sec") {
		position = std::chrono::duration_cast<
		                std::chrono::microseconds>(
		                std::chrono::seconds(raw_time));

	} else {
		/* Assume microseconds */
		position = std::chrono::microseconds(raw_time);
	}

	/* Weed out any unwanted states */
	bool valid = CurrentStateIn({State::PLAYING, State::STOPPED});
	if (valid) {
		// enum state current_state = this->cstate;

		// cmd_stop(); // We need the player engine stopped in order to
		// seek
		this->au->SeekToPosition(position);
		this->position_last_invalid = true;

		// if (current_state == S_PLAY) {
		// If we were playing before we'd ideally like to resume
		// cmd_play();
		//}
	}

	return valid;
}

State Player::CurrentState()
{
	return this->current_state;
}

/* Performs an iteration of the player update loop. */
void Player::Update()
{
	if (this->current_state == State::PLAYING) {
		if (this->au->IsHalted()) {
			Eject();
		} else {
			SendPositionIfReady();
		}
	}
	if (CurrentStateIn({State::PLAYING, State::STOPPED})) {
		bool more = this->au->Update();
		if (!more) {
			Eject();
		}
	}
}

/* Throws an error if the current state is not in the state set provided by
 * the initializer_list.
 */
bool Player::CurrentStateIn(std::initializer_list<State> states)
{
	return std::any_of(states.begin(), states.end(), [this](State state) {
		return this->current_state == state;
	});
}

/* Sets the player state and honks accordingly. */
void Player::SetState(State state)
{
	State last_state = this->current_state;

	this->current_state = state;

	if (this->state_listener != nullptr) {
		this->state_listener(last_state, state);
	}
}

/**
 * Registers a listener for state changes.
 * @param listener The function to which state change signals shall be sent.
 */
void Player::RegisterStateListener(StateListener listener)
{
	this->state_listener = listener;
}

/**
 * Sends a position signal to the outside environment, if ready to send one.
 * This only sends a signal if the requested amount of time has passed since the
 * last one.
 * It requires a handler to have been registered via SetTimeSignalHandler.
 */
void Player::SendPositionIfReady()
{
	auto position = this->au->CurrentPosition<std::chrono::microseconds>();
	if (IsReadyToSendPosition(position)) {
		this->position_listener(position);
		this->position_last = position;
		this->position_last_invalid = false;
	}
}

/**
 * Figures out whether it's time to send a position signal.
 * @param current_position The current position in the song.
 * @return True if enough time has elapsed for a signal to be sent; false
 * otherwise.
 */
bool Player::IsReadyToSendPosition(std::chrono::microseconds current_position)
{
	bool ready = false;

	if (this->position_last_invalid) {
		ready = true;
	} else if (this->position_listener != nullptr) {
		auto difference = current_position - this->position_last;
		ready = difference >= this->position_period;
	}

	return ready;
}

/**
 * Registers a listener for position signals.
 * @param listener The function to which position signals shall be sent.
 * @param period_usecs The approximate period, in microseconds, between position
 * signals.
 */
void Player::RegisterPositionListener(PositionListener listener,
                                      const std::chrono::microseconds period)
{
	this->position_listener = listener;
	this->position_period = period;
}
<|endoftext|>


<commit_before>#include "NegativeBinomialRand.h"

template< typename T >
NegativeBinomialRand<T>::NegativeBinomialRand(T number, double probability) :
    G(probability)
{
    setParameters(number, probability);
}

template< typename T >
std::string NegativeBinomialRand<T>::name()
{
    return "Negative Binomial(" + toStringWithPrecision(getNumber()) + ", " + toStringWithPrecision(getProbability()) + ")";
}

template< typename T >
void NegativeBinomialRand<T>::setParameters(T number, double probability)
{
    r = std::max(number, static_cast<T>(1.0));

    p = std::min(std::max(probability, MIN_POSITIVE), 1.0);
    G.setProbability(1 - p);

    pdfCoef = std::pow(1 - p, r) / std::tgamma(r);
    Y.setParameters(r, p / (1 - p));
}

template <>
double NegativeBinomialRand<double>::P(int k) const
{
    return (k < 0) ? 0 : pdfCoef * std::tgamma(r + k) / RandMath::factorial(k) * std::pow(p, k);
}

template <>
double NegativeBinomialRand<int>::P(int k) const
{
    return (k < 0) ? 0 : pdfCoef * RandMath::factorial(r + k - 1) / RandMath::factorial(k) * std::pow(p, k);
}

template< typename T >
double NegativeBinomialRand<T>::F(double x) const
{
    return 1.0 - RandMath::incompleteBetaFun(p, std::floor(x) + 1, r);
}

template<>
double NegativeBinomialRand<double>::variate() const
{
    return variateThroughGammaPoisson();
}

template<>
double NegativeBinomialRand<int>::variate() const
{
    if (r < 10)
        return variateThroughGeometric();
    return variateThroughGammaPoisson();
}

template< typename T >
double NegativeBinomialRand<T>::variateThroughGeometric() const
{
    double res = 0;
    for (int i = 0; i != static_cast<int>(r); ++i)
        res += G.variate();
    return res;
}

template< typename T >
double NegativeBinomialRand<T>::variateThroughGammaPoisson() const
{
    return PoissonRand::variate(Y.variate());
}

template< typename T >
double NegativeBinomialRand<T>::Mean() const
{
    return p * r / (1 - p);
}

template< typename T >
double NegativeBinomialRand<T>::Variance() const
{
    return Mean() / (1 - p);
}

template< typename T >
double NegativeBinomialRand<T>::Mode() const
{
    return (r > 1) ? std::floor((r - 1) * p / (1 - p)) : 0;
}

template< typename T >
double NegativeBinomialRand<T>::Skewness() const
{
    return (1 + p) / std::sqrt(p * r);
}

template< typename T >
double NegativeBinomialRand<T>::ExcessKurtosis() const
{
    double kurtosis = (1 - p);
    kurtosis *= kurtosis;
    kurtosis /= p;
    kurtosis += 6;
    return kurtosis / r;
}

template class NegativeBinomialRand<int>;
template class NegativeBinomialRand<double>;
<commit_msg>Update NegativeBinomialRand.cpp<commit_after>#include "NegativeBinomialRand.h"

template< typename T >
NegativeBinomialRand<T>::NegativeBinomialRand(T number, double probability) :
    G(probability)
{
    setParameters(number, probability);
}

template< typename T >
std::string NegativeBinomialRand<T>::name()
{
    return "Negative Binomial(" + toStringWithPrecision(getNumber()) + ", " + toStringWithPrecision(getProbability()) + ")";
}

template< typename T >
void NegativeBinomialRand<T>::setParameters(T number, double probability)
{
    r = std::max(number, static_cast<T>(1.0));

    p = std::min(std::max(probability, MIN_POSITIVE), 1.0);
    G.setProbability(1 - p);

    pdfCoef = std::pow(1 - p, r) / std::tgamma(r);
    Y.setParameters(r, p / (1 - p));
}

template <>
double NegativeBinomialRand<double>::P(int k) const
{
    return (k < 0) ? 0 : pdfCoef * std::tgamma(r + k) / RandMath::factorial(k) * std::pow(p, k);
}

template <>
double NegativeBinomialRand<int>::P(int k) const
{
    return (k < 0) ? 0 : pdfCoef * RandMath::factorial(r + k - 1) / RandMath::factorial(k) * std::pow(p, k);
}

template< typename T >
double NegativeBinomialRand<T>::F(double x) const
{
    return 1.0 - RandMath::incompleteBetaFun(p, std::floor(x) + 1, r);
}

template<>
double NegativeBinomialRand<double>::variate() const
{
    return variateThroughGammaPoisson();
}

template<>
double NegativeBinomialRand<int>::variate() const
{
    if (r < 10)
        return variateThroughGeometric();
    return variateThroughGammaPoisson();
}

template< typename T >
double NegativeBinomialRand<T>::variateThroughGeometric() const
{
    double res = 0;
    for (int i = 0; i != static_cast<int>(r); ++i)
        res += G.variate();
    return res;
}

template< typename T >
double NegativeBinomialRand<T>::variateThroughGammaPoisson() const
{
    return PoissonRand::variate(Y.variate());
}

template< typename T >
double NegativeBinomialRand<T>::Mean() const
{
    return p * r / (1 - p);
}

template< typename T >
double NegativeBinomialRand<T>::Variance() const
{
    return Mean() / (1 - p);
}

template< typename T >
std::complex<double> NegativeBinomialRand<T>::CF(double t) const
{
    double numerator = 1 - p;
    std::complex denominator(0, t);
    denominator = 1 - p * std::exp(denominator);
    return std::pow(numerator / denominator, r);
}

template< typename T >
double NegativeBinomialRand<T>::Mode() const
{
    return (r > 1) ? std::floor((r - 1) * p / (1 - p)) : 0;
}

template< typename T >
double NegativeBinomialRand<T>::Skewness() const
{
    return (1 + p) / std::sqrt(p * r);
}

template< typename T >
double NegativeBinomialRand<T>::ExcessKurtosis() const
{
    double kurtosis = (1 - p);
    kurtosis *= kurtosis;
    kurtosis /= p;
    kurtosis += 6;
    return kurtosis / r;
}

template class NegativeBinomialRand<int>;
template class NegativeBinomialRand<double>;
<|endoftext|>


<commit_before>/**
 * \file dcs/math/curvefit/interpolation/base1d.hpp
 *
 * \brief Base class for one-dimensional interpolation.
 *
 * \author Marco Guazzone (marco.guazzone@gmail.com)
 *
 *

 *
 * Copyright (C) 2013       Marco Guazzone (marco.guazzone@gmail.com)
 *                          [Distributed Computing System (DCS) Group,
 *                           Computer Science Institute,
 *                           Department of Science and Technological Innovation,
 *                           University of Piemonte Orientale,
 *                           Alessandria (Italy)]
 *
 * This file is part of dcsxx-commons (below referred to as "this program").
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef DCS_MATH_CURVEFIT_INTERPOLATION_BASE1D_HPP
#define DCS_MATH_CURVEFIT_INTERPOLATION_BASE1D_HPP

#include <algorithm>
#include <cstddef>
#include <cmath>
#include <dcs/assert.hpp>
#include <dcs/debug.hpp>
#include <dcs/exception.hpp>
#include <dcs/math/traits/float.hpp>
#include <stdexcept>
#include <vector>

//#define DCS_MATH_CURVEFIT_INTERPOLATION_USE_CORRELATION_DRIVEN_SEARCH_METHOD 1

namespace dcs { namespace math { namespace curvefit {

template <typename RealT>
class base_1d_interpolator
{
	public: typedef RealT real_type;

	public: template <typename XIterT, typename YIterT>
			base_1d_interpolator(XIterT first_x, XIterT last_x, YIterT first_y, YIterT last_y, ::std::size_t order, ::std::size_t m)
	: xx_(first_x, last_x),
	  yy_(first_y, last_y),
	  n_(xx_.size()),
	  ord_(order),
	  m_(m)
#ifdef DCS_MATH_CURVEFIT_INTERPOLATION_USE_CORRELATION_DRIVEN_SEARCH_METHOD
	 ,jsav_(0),
	  cor_(false),
	  dj_(::std::max(::std::size_t(1), static_cast< ::std::size_t >(::std::pow(n_, 0.25))))
#endif // DCS_MATH_CURVEFIT_INTERPOLATION_USE_CORRELATION_DRIVEN_SEARCH_METHOD
	{
		DCS_ASSERT(n_ >= 2,
				   DCS_EXCEPTION_THROW(::std::invalid_argument,
									   "Invalid number of interpolating points"));
		DCS_ASSERT(ord_ >= 0,
				   DCS_EXCEPTION_THROW(::std::invalid_argument,
									   "Invalid interpolation order"));
		DCS_ASSERT(m_ >= 2,
				   DCS_EXCEPTION_THROW(::std::invalid_argument,
									   "Invalid number of local points"));
//		DCS_ASSERT(m_ >= ord_,
//				   DCS_EXCEPTION_THROW(::std::invalid_argument,
//									   "Interpolation order cannot be greater than the number of local points"));
		DCS_ASSERT(m_ <= n_,
				   DCS_EXCEPTION_THROW(::std::invalid_argument,
									   "Number of local points cannot be greater than the number of interpolating points"));
	}

	public: real_type operator()(real_type x) const
	{
		return do_interpolate(x);
	}

	public: ::std::size_t order() const
	{
		return ord_;
	}

	public: ::std::size_t num_nodes() const
	{
		return xx_.size();
	}

	public: ::std::size_t num_values() const
	{
		return yy_.size();
	}

	public: ::std::vector<real_type> nodes() const
	{
		return xx_;
	}

	public: ::std::vector<real_type> values() const
	{
		return yy_();
	}

	public: real_type node(::std::size_t i) const
	{
		return xx_[i];
	}

	public: real_type value(::std::size_t i) const
	{
		return yy_[i];
	}

	// Find the position k of the interval [xx_k,xx_{k+1}] where the given x falls such that
	//   'xx[k] <= x < xx[k+1],
	// for all 'x' within the table.
	// If 'x < xx[0]' then 'k' is 1.  If 'x >= xx[n-1]' then 'k' is 'n-1'
	protected: ::std::size_t find(real_type x) const
	{
#ifdef DCS_MATH_CURVEFIT_INTERPOLATION_USE_CORRELATION_DRIVEN_SEARCH_METHOD
		// Experimental
		return cor_ ? hunt(x) : locate(x);
#else // DCS_MATH_CURVEFIT_INTERPOLATION_USE_CORRELATION_DRIVEN_SEARCH_METHOD
		//return sequential_find(x);
		return bsearch_find(x);
#endif // DCS_MATH_CURVEFIT_INTERPOLATION_USE_CORRELATION_DRIVEN_SEARCH_METHOD
	}

#ifdef DCS_MATH_CURVEFIT_INTERPOLATION_USE_CORRELATION_DRIVEN_SEARCH_METHOD

	/// Find the location of a given value using the binary search method.
	protected: ::std::size_t locate(real_type x) const
	{
		bool ascnd(xx_[n_-1] >= xx_[0]);
		::std::size_t jl(0);
		::std::size_t ju(n_-1);
		while ((ju-jl) > 1)
		{
			//::std::size_t jm((ju+jl) >> 1);
			long jm((ju+jl) >> 1);
			if ((x >= xx_[jm]) == ascnd)
			{
				jl = jm;
			}
			else
			{
				ju = jm;
			}
		}
		cor_ = ::std::abs(jl-jsav_) > dj_ ? false : true;
		jsav_ = jl;
		return ::std::max(::std::ptrdiff_t(0), ::std::min(static_cast< ::std::ptrdiff_t >(n_-m_), static_cast< ::std::ptrdiff_t >(jl-((m_-2) >> 1))));
	}

	/// Bracket a specified value inside an interval.
	protected: ::std::size_t hunt(real_type x) const
	{
		bool ascnd(xx_[n_-1] >= xx_[0]);
		::std::ptrdiff_t jl(jsav_);
		::std::ptrdiff_t ju(0);
		::std::size_t inc(1);

		if (jl < 0 || jl > (n_-1))
		{
			jl = 0;
			ju = n_-1;
		}
		else if ((x >= xx_[jl]) == ascnd)
		{
			while (true)
			{
				ju = jl + inc;
				if (ju >= (n_-1))
				{
					ju = n_-1;
					break;
				}
				else if ((x < xx_[ju]) == ascnd)
				{
					break;
				}
				else
				{
					jl = ju;
					inc *= 2;
				}
			}
		}
		else
		{
			ju = jl;
			while (true)
			{
				jl = jl - inc;
				if (jl <= 0)
				{
					jl = 0;
					break;
				}
				else if ((x >= xx_[jl]) == ascnd)
				{
					break;
				}
				else
				{
					ju = jl;
					inc *= 2;
				}
			}
		}
		while ((ju-jl) > 1)
		{
			::std::size_t jm((ju+jl) >> 1);
			if ((x >= xx_[jm]) == ascnd)
			{
				jl = jm;
			}
			else
			{
				ju = jm;
			}
		}
		cor_ = ::std::abs(jl-jsav_) > dj_ ? false : true;
		jsav_ = jl;
		return ::std::max(::std::ptrdiff_t(0), ::std::min(static_cast< ::std::ptrdiff_t >(n_-m_), static_cast< ::std::ptrdiff_t >(jl-((m_-2) >> 1))));
	}

#else // DCS_MATH_CURVEFIT_INTERPOLATION_USE_CORRELATION_DRIVEN_SEARCH_METHOD

	/// Locate a given value using a sequential search
	protected: ::std::size_t sequential_find(real_type x) const
	{
		if (::dcs::math::float_traits<real_type>::approximately_less_equal(x, xx_[0]))
		{
			return 0;
		}
		for (::std::size_t i = 1; i < n_; ++i)
		{
			if (::dcs::math::float_traits<real_type>::approximately_less_equal(x, xx_[i]))
			{
				return i-1;
			}
		}
		return n_-1;
	}

	/// Locate a given value by binary search
	protected: ::std::size_t bsearch_find(real_type x) const
	{
		// Handle out-of-domain points
		if (::dcs::math::float_traits<real_type>::approximately_less_equal(x, xx_[0]))
		{
			return 0;
		}
		if (::dcs::math::float_traits<real_type>::approximately_greater_equal(x, xx_[n_-1]))
		{
			return n_-1;
		}

		::std::size_t lo(0);
		::std::size_t hi(n_-1);

		while (lo < (hi-1))
		{
			const ::std::size_t mid((hi+lo) >> 1);
			if (::dcs::math::float_traits<real_type>::definitely_less(x, xx_[mid]))
			{
				hi = mid;
			}
			else
			{
				lo = mid;
			}
		}

		return lo;
	}

#endif // DCS_MATH_CURVEFIT_INTERPOLATION_USE_CORRELATION_DRIVEN_SEARCH_METHOD

	private: virtual real_type do_interpolate(real_type x) const = 0;

	private: const ::std::vector<real_type> xx_; ///< Data points
	private: const ::std::vector<real_type> yy_; ///< Data values
//	private: const ::std::size_t n_; ///< The number of interpolating points
	private: const long n_; ///< The number of interpolating points
	private: const ::std::size_t ord_; ///< The order of interpolation
//	private: const ::std::size_t m_; ///< The number of local points
	private: const long m_; ///< The number of local points
#ifdef DCS_MATH_CURVEFIT_INTERPOLATION_USE_CORRELATION_DRIVEN_SEARCH_METHOD
//	private: mutable ::std::size_t jsav_;
//	private: mutable ::std::size_t cor_;
//	private: const ::std::size_t dj_;
	private: mutable long jsav_;
	private: mutable bool cor_;
	private: const long dj_;
#endif // DCS_MATH_CURVEFIT_INTERPOLATION_USE_CORRELATION_DRIVEN_SEARCH_METHOD
}; // base_1d_interpolator

}}} // Namespace dcs::math::curvefit

#endif // DCS_MATH_CURVEFIT_INTERPOLATION_BASE1D_HPP
<commit_msg>(new:minor) Improved doc for bsearch_find method<commit_after>/**
 * \file dcs/math/curvefit/interpolation/base1d.hpp
 *
 * \brief Base class for one-dimensional interpolation.
 *
 * \author Marco Guazzone (marco.guazzone@gmail.com)
 *
 *

 *
 * Copyright (C) 2013       Marco Guazzone (marco.guazzone@gmail.com)
 *                          [Distributed Computing System (DCS) Group,
 *                           Computer Science Institute,
 *                           Department of Science and Technological Innovation,
 *                           University of Piemonte Orientale,
 *                           Alessandria (Italy)]
 *
 * This file is part of dcsxx-commons (below referred to as "this program").
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef DCS_MATH_CURVEFIT_INTERPOLATION_BASE1D_HPP
#define DCS_MATH_CURVEFIT_INTERPOLATION_BASE1D_HPP

#include <algorithm>
#include <cstddef>
#include <cmath>
#include <dcs/assert.hpp>
#include <dcs/debug.hpp>
#include <dcs/exception.hpp>
#include <dcs/math/traits/float.hpp>
#include <stdexcept>
#include <vector>

//#define DCS_MATH_CURVEFIT_INTERPOLATION_USE_CORRELATION_DRIVEN_SEARCH_METHOD 1

namespace dcs { namespace math { namespace curvefit {

template <typename RealT>
class base_1d_interpolator
{
	public: typedef RealT real_type;

	public: template <typename XIterT, typename YIterT>
			base_1d_interpolator(XIterT first_x, XIterT last_x, YIterT first_y, YIterT last_y, ::std::size_t order, ::std::size_t m)
	: xx_(first_x, last_x),
	  yy_(first_y, last_y),
	  n_(xx_.size()),
	  ord_(order),
	  m_(m)
#ifdef DCS_MATH_CURVEFIT_INTERPOLATION_USE_CORRELATION_DRIVEN_SEARCH_METHOD
	 ,jsav_(0),
	  cor_(false),
	  dj_(::std::max(::std::size_t(1), static_cast< ::std::size_t >(::std::pow(n_, 0.25))))
#endif // DCS_MATH_CURVEFIT_INTERPOLATION_USE_CORRELATION_DRIVEN_SEARCH_METHOD
	{
		DCS_ASSERT(n_ >= 2,
				   DCS_EXCEPTION_THROW(::std::invalid_argument,
									   "Invalid number of interpolating points"));
		DCS_ASSERT(ord_ >= 0,
				   DCS_EXCEPTION_THROW(::std::invalid_argument,
									   "Invalid interpolation order"));
		DCS_ASSERT(m_ >= 2,
				   DCS_EXCEPTION_THROW(::std::invalid_argument,
									   "Invalid number of local points"));
//		DCS_ASSERT(m_ >= ord_,
//				   DCS_EXCEPTION_THROW(::std::invalid_argument,
//									   "Interpolation order cannot be greater than the number of local points"));
		DCS_ASSERT(m_ <= n_,
				   DCS_EXCEPTION_THROW(::std::invalid_argument,
									   "Number of local points cannot be greater than the number of interpolating points"));
	}

	public: real_type operator()(real_type x) const
	{
		return do_interpolate(x);
	}

	public: ::std::size_t order() const
	{
		return ord_;
	}

	public: ::std::size_t num_nodes() const
	{
		return xx_.size();
	}

	public: ::std::size_t num_values() const
	{
		return yy_.size();
	}

	public: ::std::vector<real_type> nodes() const
	{
		return xx_;
	}

	public: ::std::vector<real_type> values() const
	{
		return yy_();
	}

	public: real_type node(::std::size_t i) const
	{
		return xx_[i];
	}

	public: real_type value(::std::size_t i) const
	{
		return yy_[i];
	}

	// Find the position k of the interval [xx_k,xx_{k+1}] where the given x falls such that
	//   'xx[k] <= x < xx[k+1],
	// for all 'x' within the table.
	// If 'x < xx[0]' then 'k' is 1.  If 'x >= xx[n-1]' then 'k' is 'n-1'
	protected: ::std::size_t find(real_type x) const
	{
#ifdef DCS_MATH_CURVEFIT_INTERPOLATION_USE_CORRELATION_DRIVEN_SEARCH_METHOD
		// Experimental
		return cor_ ? hunt(x) : locate(x);
#else // DCS_MATH_CURVEFIT_INTERPOLATION_USE_CORRELATION_DRIVEN_SEARCH_METHOD
		//return sequential_find(x);
		return bsearch_find(x);
#endif // DCS_MATH_CURVEFIT_INTERPOLATION_USE_CORRELATION_DRIVEN_SEARCH_METHOD
	}

#ifdef DCS_MATH_CURVEFIT_INTERPOLATION_USE_CORRELATION_DRIVEN_SEARCH_METHOD

	/// Find the location of a given value using the binary search method.
	protected: ::std::size_t locate(real_type x) const
	{
		bool ascnd(xx_[n_-1] >= xx_[0]);
		::std::size_t jl(0);
		::std::size_t ju(n_-1);
		while ((ju-jl) > 1)
		{
			//::std::size_t jm((ju+jl) >> 1);
			long jm((ju+jl) >> 1);
			if ((x >= xx_[jm]) == ascnd)
			{
				jl = jm;
			}
			else
			{
				ju = jm;
			}
		}
		cor_ = ::std::abs(jl-jsav_) > dj_ ? false : true;
		jsav_ = jl;
		return ::std::max(::std::ptrdiff_t(0), ::std::min(static_cast< ::std::ptrdiff_t >(n_-m_), static_cast< ::std::ptrdiff_t >(jl-((m_-2) >> 1))));
	}

	/// Bracket a specified value inside an interval.
	protected: ::std::size_t hunt(real_type x) const
	{
		bool ascnd(xx_[n_-1] >= xx_[0]);
		::std::ptrdiff_t jl(jsav_);
		::std::ptrdiff_t ju(0);
		::std::size_t inc(1);

		if (jl < 0 || jl > (n_-1))
		{
			jl = 0;
			ju = n_-1;
		}
		else if ((x >= xx_[jl]) == ascnd)
		{
			while (true)
			{
				ju = jl + inc;
				if (ju >= (n_-1))
				{
					ju = n_-1;
					break;
				}
				else if ((x < xx_[ju]) == ascnd)
				{
					break;
				}
				else
				{
					jl = ju;
					inc *= 2;
				}
			}
		}
		else
		{
			ju = jl;
			while (true)
			{
				jl = jl - inc;
				if (jl <= 0)
				{
					jl = 0;
					break;
				}
				else if ((x >= xx_[jl]) == ascnd)
				{
					break;
				}
				else
				{
					ju = jl;
					inc *= 2;
				}
			}
		}
		while ((ju-jl) > 1)
		{
			::std::size_t jm((ju+jl) >> 1);
			if ((x >= xx_[jm]) == ascnd)
			{
				jl = jm;
			}
			else
			{
				ju = jm;
			}
		}
		cor_ = ::std::abs(jl-jsav_) > dj_ ? false : true;
		jsav_ = jl;
		return ::std::max(::std::ptrdiff_t(0), ::std::min(static_cast< ::std::ptrdiff_t >(n_-m_), static_cast< ::std::ptrdiff_t >(jl-((m_-2) >> 1))));
	}

#else // DCS_MATH_CURVEFIT_INTERPOLATION_USE_CORRELATION_DRIVEN_SEARCH_METHOD

	/// Locate a given value using a sequential search
	protected: ::std::size_t sequential_find(real_type x) const
	{
		if (::dcs::math::float_traits<real_type>::approximately_less_equal(x, xx_[0]))
		{
			return 0;
		}
		for (::std::size_t i = 1; i < n_; ++i)
		{
			if (::dcs::math::float_traits<real_type>::approximately_less_equal(x, xx_[i]))
			{
				return i-1;
			}
		}
		return n_-1;
	}

	/**
	 * Locate a given value by binary search
	 *
	 * The resulting index \c j is guaranteed to be strictly less than the max
	 * number of nodes and greater than or equal to 0, so that the implicit bracket
	 * [j,j+1] always corresponds to a region within the implicit value
	 * range of the value array.
	 *
	 * Specifically, suppose the node array is
	 * \f$k=\{k_0, k_1, \ldots, k_m\}\f$, the index returned by this
	 * function is:
	 * \f{equation}
	 * \begin{cases}
	 * 0, & x \le k_0,\\
	 * j, & x > k_{j-1} && x \le k_j,\\
	 * m-1, & x \ge k_m,\\
	 * \end{cases}
	 * \f}
	 */
	protected: ::std::size_t bsearch_find(real_type x) const
	{
		// Handle out-of-domain points
		if (::dcs::math::float_traits<real_type>::approximately_less_equal(x, xx_[0]))
		{
			return 0;
		}
		if (::dcs::math::float_traits<real_type>::approximately_greater_equal(x, xx_[n_-1]))
		{
			return n_-1;
		}

		::std::size_t lo(0);
		::std::size_t hi(n_-1);

		while (lo < (hi-1))
		{
			const ::std::size_t mid((hi+lo) >> 1);
			if (::dcs::math::float_traits<real_type>::definitely_less(x, xx_[mid]))
			{
				hi = mid;
			}
			else
			{
				lo = mid;
			}
		}

		return lo;
	}

#endif // DCS_MATH_CURVEFIT_INTERPOLATION_USE_CORRELATION_DRIVEN_SEARCH_METHOD

	private: virtual real_type do_interpolate(real_type x) const = 0;

	private: const ::std::vector<real_type> xx_; ///< Data points
	private: const ::std::vector<real_type> yy_; ///< Data values
//	private: const ::std::size_t n_; ///< The number of interpolating points
	private: const long n_; ///< The number of interpolating points
	private: const ::std::size_t ord_; ///< The order of interpolation
//	private: const ::std::size_t m_; ///< The number of local points
	private: const long m_; ///< The number of local points
#ifdef DCS_MATH_CURVEFIT_INTERPOLATION_USE_CORRELATION_DRIVEN_SEARCH_METHOD
//	private: mutable ::std::size_t jsav_;
//	private: mutable ::std::size_t cor_;
//	private: const ::std::size_t dj_;
	private: mutable long jsav_;
	private: mutable bool cor_;
	private: const long dj_;
#endif // DCS_MATH_CURVEFIT_INTERPOLATION_USE_CORRELATION_DRIVEN_SEARCH_METHOD
}; // base_1d_interpolator

}}} // Namespace dcs::math::curvefit

#endif // DCS_MATH_CURVEFIT_INTERPOLATION_BASE1D_HPP
<|endoftext|>


<commit_before>/***********************************************************************************************************************
**
** Copyright (c) 2011, 2013 ETH Zurich
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
** following conditions are met:
**
**    * Redistributions of source code must retain the above copyright notice, this list of conditions and the following
**      disclaimer.
**    * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
**      following disclaimer in the documentation and/or other materials provided with the distribution.
**    * Neither the name of the ETH Zurich nor the names of its contributors may be used to endorse or promote products
**      derived from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
** INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
** DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
** SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**
***********************************************************************************************************************/

#include "Node.h"
#include "../modelbase.h"
#include "../model/Model.h"
#include "commands/UndoCommand.h"
#include "ModelException.h"
#include "Reference.h"
#include "Core/src/AdapterManager.h"

using namespace Logger;

namespace Model {

::Core::InitializationRegistry& nodeTypeInitializationRegistry();
DEFINE_TYPE_ID_BASE(Node, nodeTypeInitializationRegistry, "Node",)

/***********************************************************************************************************************
 * STATIC MEMBERS
 **********************************************************************************************************************/
int Node::numRegisteredTypes_ = 0;
QHash<QString, Node::NodeConstructor> Node::nodeConstructorRegister;
QHash<QString, Node::NodePersistenceConstructor> Node::nodePersistenceConstructorRegister;

QSet<const Node*>& Node::partiallyLoadedNodes()
{
	static QSet<const Node*> set;
	return set;
}

/***********************************************************************************************************************
 * CONSTRUCTORS AND DESTRUCTORS
 **********************************************************************************************************************/
Node::Node(Node* parent) : parent_{parent}
{
	if (parent && !parent->isModifyable())
		throw ModelException("Trying to create a node with an non-modifiable parent.");
}

Node::~Node()
{
	partiallyLoadedNodes().remove(this);
}

Node* Node::createDefaultInstance(Node*)
{
	Q_ASSERT(false);
	return nullptr;
}

/***********************************************************************************************************************
 * MAIN METHODS
 **********************************************************************************************************************/
void Node::execute(UndoCommand *command)
{
	if ( this != command->target() )
		throw ModelException("Command target differs from current node when executing commands");

	Model* m = model();

	if (m)
	{
		if ( !m->canBeModified(this) ) throw ModelException("Can not modify the current node.");
		m->pushCommandOnUndoStack(command);
	}
	else
	{
		command->redo();
		SAFE_DELETE(command);
	}
}

Node* Node::lowestCommonAncestor(Node* other)
{
	QList<Node*> thisParents;
	QList<Node*> otherParents;

	// Get all parents of the current node
	Node* n = this;
	while ( n )
	{
		thisParents.prepend(n);
		n = n->parent();
	}

	// Get all parents of the other node
	n = other;
	while ( n )
	{
		otherParents.prepend(n);
		n = n->parent();
	}

	// Find the lowest common ancestor
	n = nullptr;
	while ( thisParents.size() > 0 && otherParents.size() > 0 && thisParents.first() == otherParents.first() )
	{
		n = thisParents.first();

		thisParents.removeFirst();
		otherParents.removeFirst();
	}

	return n;
}

bool Node::isAncestorOf(const Node* other) const
{
	if (other == nullptr) return false;

	const Node* p = other->parent();

	while (p && p != this) p = p->parent();

	return p == this;
}

Node* Node::childToSubnode(const Node* other) const
{
	if (other == nullptr) return nullptr;

	const Node* child = other;
	const Node* parent = child->parent();

	while (parent && parent != this)
	{
		child = parent;
		parent = child->parent();
	}

	return parent == this ? const_cast<Node*>(child) : nullptr;
}

bool Node::isModifyable() const
{
	Model* m = model();

	return !m || m->canBeModified(this);
}

bool Node::replaceChild(Node*, Node*)
{
	return false;
}

bool Node::findSymbols(QSet<Node*>& result, const SymbolMatcher& matcher, Node* source, FindSymbolDirection direction,
		SymbolTypes symbolTypes, bool exhaustAllScopes)
{
	bool found{};

	if (direction == SEARCH_HERE)
	{
		if (symbolMatches(matcher, symbolTypes))
		{
			found = true;
			result.insert(this);
		}
	}
	else if (direction == SEARCH_DOWN)
	{
		for (auto c : childrenInScope())
			found = c->findSymbols(result, matcher, source, SEARCH_HERE, symbolTypes, false) || found;
	}
	else if (direction == SEARCH_UP)
	{
		auto ignore = childToSubnode(source);
		for (auto c : childrenInScope())
			if (c != ignore) // Optimize the search by skipping this scope, since we've already searched there
				found = c->findSymbols(result, matcher, source, SEARCH_HERE, symbolTypes, false) || found;

		if ((exhaustAllScopes || !found) && symbolMatches(matcher, symbolTypes))
		{
			found = true;
			result.insert(this);
		}

		if ((exhaustAllScopes || !found) && parent_)
			found = parent_->findSymbols(result, matcher, source, SEARCH_UP, symbolTypes, exhaustAllScopes) || found;
	}

	return found;
}

QList<Node*> Node::childrenInScope() const
{
	return children();
}

void Node::beginModification(const QString &text)
{
	if (auto m = model())
		m->beginModification(this, text);
}

void Node::endModification()
{
	if (auto m = model()) m->endModification();
}

QString Node::toDebugString()
{
	auto ntdsa = Core::AdapterManager::adapt<NodeToDebugStringAdapter>(this);

	QString ret = ntdsa ? ntdsa->str : "no debug string for node";
	SAFE_DELETE(ntdsa);
	return ret;
}

bool Node::hasPartiallyLoadedChildren() const
{
	if (isPartiallyLoaded()) return true;

	return false;
}
/***********************************************************************************************************************
 * GETTERS AND SETTERS
 **********************************************************************************************************************/
void Node::setParent(Node* parent)
{
	//TODO: is this operation efficient and even possible when performed on top level objects such as namespaces and
	// packages?

	auto mOld = model();
	auto mNew = parent ? parent->model() : nullptr;

	if (mOld || mNew)
	{
		QList<Node*> queue;
		queue.append(this);

		// Transfer unresolved references from the old model to the new one
		while (!queue.isEmpty())
		{
			if (auto r = dynamic_cast<Reference*>(queue.first()))
			{
				if (!r->isResolved() )
				{
					if (mOld) mOld->removeUnresolvedReference(r);
					if (mNew) mNew->addUnresolvedReference(r);
				}
			}

			queue.append(queue.first()->children());
			queue.removeFirst();
		}
	}

	parent_ = parent;
}

QList<Node*> Node::children() const
{
	return QList<Node*>();
}

bool Node::definesSymbol() const
{
	return false;
}

const QString& Node::symbolName() const
{
	static QString nullString;
	return nullString;
}

Node::SymbolTypes Node::symbolType() const
{
	return UNSPECIFIED;
}

bool Node::isNewPersistenceUnit() const
{
	return false;
}

Node* Node::persistentUnitNode() const
{
	const Node* persistentUnitNode = this;
	const Node* prev = this;

	while ( persistentUnitNode && persistentUnitNode->isNewPersistenceUnit() == false )
	{
		prev = persistentUnitNode;
		persistentUnitNode = persistentUnitNode->parent();
	}

	if (persistentUnitNode) return const_cast<Node*> (persistentUnitNode);
	else return const_cast<Node*> (prev);
}

int Node::revision() const
{
	return revision_;
}

void Node::incrementRevision()
{
	revision_++;
}

void Node::addToRevision(int valueToAdd)
{
	revision_ += valueToAdd;
}

NodeReadWriteLock* Node::accessLock() const
{
	if ( parent_ ) return parent_->accessLock();
	else
		return model()->rootLock();
}

QList<UsedLibrary*> Node::usedLibraries()
{
	QList<UsedLibrary*> all;
	for(auto c : children())
		all << c->usedLibraries();
	return all;
}

/***********************************************************************************************************************
 * STATIC METHODS
 **********************************************************************************************************************/
int Node::registerNodeType(const QString &type, const NodeConstructor constructor,
		const NodePersistenceConstructor persistenceconstructor)
{
	if ( isTypeRegistered(type) )
		throw ModelException("Trying to register a node type that has already been registered: " + type);

	nodeConstructorRegister.insert(type, constructor);
	nodePersistenceConstructorRegister.insert(type, persistenceconstructor);

	ModelBase::log()->add(Log::LOGINFO, "Registered new node type " + type);

	++numRegisteredTypes_;
	return numRegisteredTypes_; // Id 0 is reserved for Node
}

Node* Node::createNewNode(const QString &type, Node* parent)
{
	auto iter = nodeConstructorRegister.find(type);
	if ( iter != nodeConstructorRegister.end() )
	{
		return iter.value()(parent);
	}
	else
	{
		ModelBase::log()->add(Log::LOGERROR, "Could not create new node. Requested node type '"
				+ type +"' has not been registered.");
		return nullptr;
	}
}

Node* Node::createNewNode(const QString &type, Node* parent, PersistentStore &store, bool partialLoadHint)
{
	auto iter = nodePersistenceConstructorRegister.find(type);
	if ( iter != nodePersistenceConstructorRegister.end() )
	{
		return iter.value()(parent, store, partialLoadHint);
	}
	else
	{
		ModelBase::log()->add(Log::LOGERROR, "Could not create new node from persistence. Requested node type '"
				+ type + "' has not been registered.");
		return nullptr;
	}
}

bool Node::isTypeRegistered(const QString &type)
{
	return nodeConstructorRegister.contains(type);
}

}
<commit_msg>Resolve references across model boundaries by looking in libraries<commit_after>/***********************************************************************************************************************
**
** Copyright (c) 2011, 2013 ETH Zurich
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
** following conditions are met:
**
**    * Redistributions of source code must retain the above copyright notice, this list of conditions and the following
**      disclaimer.
**    * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
**      following disclaimer in the documentation and/or other materials provided with the distribution.
**    * Neither the name of the ETH Zurich nor the names of its contributors may be used to endorse or promote products
**      derived from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
** INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
** DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
** SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**
***********************************************************************************************************************/

#include "Node.h"
#include "../modelbase.h"
#include "../model/Model.h"
#include "commands/UndoCommand.h"
#include "ModelException.h"
#include "Reference.h"
#include "Core/src/AdapterManager.h"
#include "UsedLibrary.h"

using namespace Logger;

namespace Model {

::Core::InitializationRegistry& nodeTypeInitializationRegistry();
DEFINE_TYPE_ID_BASE(Node, nodeTypeInitializationRegistry, "Node",)

/***********************************************************************************************************************
 * STATIC MEMBERS
 **********************************************************************************************************************/
int Node::numRegisteredTypes_ = 0;
QHash<QString, Node::NodeConstructor> Node::nodeConstructorRegister;
QHash<QString, Node::NodePersistenceConstructor> Node::nodePersistenceConstructorRegister;

QSet<const Node*>& Node::partiallyLoadedNodes()
{
	static QSet<const Node*> set;
	return set;
}

/***********************************************************************************************************************
 * CONSTRUCTORS AND DESTRUCTORS
 **********************************************************************************************************************/
Node::Node(Node* parent) : parent_{parent}
{
	if (parent && !parent->isModifyable())
		throw ModelException("Trying to create a node with an non-modifiable parent.");
}

Node::~Node()
{
	partiallyLoadedNodes().remove(this);
}

Node* Node::createDefaultInstance(Node*)
{
	Q_ASSERT(false);
	return nullptr;
}

/***********************************************************************************************************************
 * MAIN METHODS
 **********************************************************************************************************************/
void Node::execute(UndoCommand *command)
{
	if ( this != command->target() )
		throw ModelException("Command target differs from current node when executing commands");

	Model* m = model();

	if (m)
	{
		if ( !m->canBeModified(this) ) throw ModelException("Can not modify the current node.");
		m->pushCommandOnUndoStack(command);
	}
	else
	{
		command->redo();
		SAFE_DELETE(command);
	}
}

Node* Node::lowestCommonAncestor(Node* other)
{
	QList<Node*> thisParents;
	QList<Node*> otherParents;

	// Get all parents of the current node
	Node* n = this;
	while ( n )
	{
		thisParents.prepend(n);
		n = n->parent();
	}

	// Get all parents of the other node
	n = other;
	while ( n )
	{
		otherParents.prepend(n);
		n = n->parent();
	}

	// Find the lowest common ancestor
	n = nullptr;
	while ( thisParents.size() > 0 && otherParents.size() > 0 && thisParents.first() == otherParents.first() )
	{
		n = thisParents.first();

		thisParents.removeFirst();
		otherParents.removeFirst();
	}

	return n;
}

bool Node::isAncestorOf(const Node* other) const
{
	if (other == nullptr) return false;

	const Node* p = other->parent();

	while (p && p != this) p = p->parent();

	return p == this;
}

Node* Node::childToSubnode(const Node* other) const
{
	if (other == nullptr) return nullptr;

	const Node* child = other;
	const Node* parent = child->parent();

	while (parent && parent != this)
	{
		child = parent;
		parent = child->parent();
	}

	return parent == this ? const_cast<Node*>(child) : nullptr;
}

bool Node::isModifyable() const
{
	Model* m = model();

	return !m || m->canBeModified(this);
}

bool Node::replaceChild(Node*, Node*)
{
	return false;
}

bool Node::findSymbols(QSet<Node*>& result, const SymbolMatcher& matcher, Node* source, FindSymbolDirection direction,
		SymbolTypes symbolTypes, bool exhaustAllScopes)
{
	bool found{};

	if (direction == SEARCH_HERE)
	{
		if (symbolMatches(matcher, symbolTypes))
		{
			found = true;
			result.insert(this);
		}
	}
	else if (direction == SEARCH_DOWN)
	{
		for (auto c : childrenInScope())
			found = c->findSymbols(result, matcher, source, SEARCH_HERE, symbolTypes, false) || found;
	}
	else if (direction == SEARCH_UP)
	{
		auto ignore = childToSubnode(source);
		for (auto c : childrenInScope())
			if (c != ignore) // Optimize the search by skipping this scope, since we've already searched there
				found = c->findSymbols(result, matcher, source, SEARCH_HERE, symbolTypes, false) || found;

		if ((exhaustAllScopes || !found) && symbolMatches(matcher, symbolTypes))
		{
			found = true;
			result.insert(this);
		}

		if ((exhaustAllScopes || !found) && parent_)
			found = parent_->findSymbols(result, matcher, source, SEARCH_UP, symbolTypes, exhaustAllScopes) || found;

		// Search in libraries. This is only valid for root nodes
		if ((exhaustAllScopes || !found) && !parent_)
			for(auto l : usedLibraries())
			{
				auto libRoot = l->libraryRoot();
				Q_ASSERT(libRoot);
				found = libRoot->findSymbols(result, matcher, libRoot, SEARCH_DOWN, symbolTypes, exhaustAllScopes) || found;
			}
	}

	return found;
}

QList<Node*> Node::childrenInScope() const
{
	return children();
}

void Node::beginModification(const QString &text)
{
	if (auto m = model())
		m->beginModification(this, text);
}

void Node::endModification()
{
	if (auto m = model()) m->endModification();
}

QString Node::toDebugString()
{
	auto ntdsa = Core::AdapterManager::adapt<NodeToDebugStringAdapter>(this);

	QString ret = ntdsa ? ntdsa->str : "no debug string for node";
	SAFE_DELETE(ntdsa);
	return ret;
}

bool Node::hasPartiallyLoadedChildren() const
{
	if (isPartiallyLoaded()) return true;

	return false;
}
/***********************************************************************************************************************
 * GETTERS AND SETTERS
 **********************************************************************************************************************/
void Node::setParent(Node* parent)
{
	//TODO: is this operation efficient and even possible when performed on top level objects such as namespaces and
	// packages?

	auto mOld = model();
	auto mNew = parent ? parent->model() : nullptr;

	if (mOld || mNew)
	{
		QList<Node*> queue;
		queue.append(this);

		// Transfer unresolved references from the old model to the new one
		while (!queue.isEmpty())
		{
			if (auto r = dynamic_cast<Reference*>(queue.first()))
			{
				if (!r->isResolved() )
				{
					if (mOld) mOld->removeUnresolvedReference(r);
					if (mNew) mNew->addUnresolvedReference(r);
				}
			}

			queue.append(queue.first()->children());
			queue.removeFirst();
		}
	}

	parent_ = parent;
}

QList<Node*> Node::children() const
{
	return QList<Node*>();
}

bool Node::definesSymbol() const
{
	return false;
}

const QString& Node::symbolName() const
{
	static QString nullString;
	return nullString;
}

Node::SymbolTypes Node::symbolType() const
{
	return UNSPECIFIED;
}

bool Node::isNewPersistenceUnit() const
{
	return false;
}

Node* Node::persistentUnitNode() const
{
	const Node* persistentUnitNode = this;
	const Node* prev = this;

	while ( persistentUnitNode && persistentUnitNode->isNewPersistenceUnit() == false )
	{
		prev = persistentUnitNode;
		persistentUnitNode = persistentUnitNode->parent();
	}

	if (persistentUnitNode) return const_cast<Node*> (persistentUnitNode);
	else return const_cast<Node*> (prev);
}

int Node::revision() const
{
	return revision_;
}

void Node::incrementRevision()
{
	revision_++;
}

void Node::addToRevision(int valueToAdd)
{
	revision_ += valueToAdd;
}

NodeReadWriteLock* Node::accessLock() const
{
	if ( parent_ ) return parent_->accessLock();
	else
		return model()->rootLock();
}

QList<UsedLibrary*> Node::usedLibraries()
{
	QList<UsedLibrary*> all;
	for(auto c : children())
		all << c->usedLibraries();
	return all;
}

/***********************************************************************************************************************
 * STATIC METHODS
 **********************************************************************************************************************/
int Node::registerNodeType(const QString &type, const NodeConstructor constructor,
		const NodePersistenceConstructor persistenceconstructor)
{
	if ( isTypeRegistered(type) )
		throw ModelException("Trying to register a node type that has already been registered: " + type);

	nodeConstructorRegister.insert(type, constructor);
	nodePersistenceConstructorRegister.insert(type, persistenceconstructor);

	ModelBase::log()->add(Log::LOGINFO, "Registered new node type " + type);

	++numRegisteredTypes_;
	return numRegisteredTypes_; // Id 0 is reserved for Node
}

Node* Node::createNewNode(const QString &type, Node* parent)
{
	auto iter = nodeConstructorRegister.find(type);
	if ( iter != nodeConstructorRegister.end() )
	{
		return iter.value()(parent);
	}
	else
	{
		ModelBase::log()->add(Log::LOGERROR, "Could not create new node. Requested node type '"
				+ type +"' has not been registered.");
		return nullptr;
	}
}

Node* Node::createNewNode(const QString &type, Node* parent, PersistentStore &store, bool partialLoadHint)
{
	auto iter = nodePersistenceConstructorRegister.find(type);
	if ( iter != nodePersistenceConstructorRegister.end() )
	{
		return iter.value()(parent, store, partialLoadHint);
	}
	else
	{
		ModelBase::log()->add(Log::LOGERROR, "Could not create new node from persistence. Requested node type '"
				+ type + "' has not been registered.");
		return nullptr;
	}
}

bool Node::isTypeRegistered(const QString &type)
{
	return nodeConstructorRegister.contains(type);
}

}
<|endoftext|>