<commit_before>#include "message_details_dialog.h"
#include "ui_message_details_dialog.h"

#include <rapidjson/rapidjson.h>
#include <rapidjson/document.h>
#include <rapidjson/stringbuffer.h>
#include <rapidjson/writer.h>

Message_Details_Dialog::Message_Details_Dialog(QWidget *parent, QString& message) :
    QDialog(parent),
    ui(new Ui::Message_Details_Dialog)
{
    ui->setupUi(this);

    QTableWidget* widget = ui->details_widget;
    widget->clearSelection();
    widget->clearContents();
    widget->setColumnCount(1);

    for (int i = 0; i < 3; ++i)
    {
        widget->insertRow(i);
        widget->setVerticalHeaderItem(i, new QTableWidgetItem("one"));
        widget->setItem(i, 0, new QTableWidgetItem("two"));
    }
}

Message_Details_Dialog::~Message_Details_Dialog()
{
    delete ui;
}

/*
std::vector<std::string> strip_json(const std::string& fields_to_leave, const std::vector<std::string>& json_strings)
{
    std::vector<std::string> out_strings;
    std::vector<std::string> fields;

    std::istringstream is(fields_to_leave);
    std::string part;
    while (std::getline(is, part, ','))
      fields.push_back(part);

    for (const auto& js: json_strings)
    {
        rapidjson::GenericDocument<rapidjson::UTF8<>> js_from, js_to;
        std::unique_ptr<char[]> to_parse(new char[js.size() + 1]);

        memcpy(to_parse.get(), js.c_str(), js.size());
        to_parse.get()[js.size()] = 0;

        js_to.SetObject();
        js_from.ParseInsitu(to_parse.get());

        if (js_from.IsNull())
        {
            qCritical() << "JSON document is invalid!";
            continue;
        }

        if (js_from.IsObject())
        {
            rapidjson::Value::Object jsobj(js_from.GetObject());

            for (rapidjson::Value::Object::MemberIterator it = jsobj.MemberBegin(); it != jsobj.MemberEnd(); ++it)
            {
                std::string key(it->name.GetString());
                for (const auto& field: fields)
                    if (key.compare(field) == 0)
                    {
                        js_to.AddMember(it->name, it->value, js_to.GetAllocator());
                        break;
                    }
            }
        }

        rapidjson::StringBuffer buffer;
        rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
        js_to.Accept(writer);
        out_strings.push_back(buffer.GetString());
    }

    return out_strings;
}
*/
<commit_msg>Growing message details dialog in functionality.<commit_after>#include "message_details_dialog.h"
#include "ui_message_details_dialog.h"

#include <rapidjson/rapidjson.h>
#include <rapidjson/document.h>
#include <rapidjson/stringbuffer.h>
#include <rapidjson/writer.h>

#include <QtDebug>

Message_Details_Dialog::Message_Details_Dialog(QWidget *parent, QString& message):
QDialog(parent),
ui(new Ui::Message_Details_Dialog)
{
    ui->setupUi(this);

    QTableWidget* widget = ui->details_widget;
    widget->clearSelection();
    widget->clearContents();
    widget->setColumnCount(1);

    std::string js(message.toStdString());

    rapidjson::GenericDocument<rapidjson::UTF8<>> js_from;
    std::unique_ptr<char[]> to_parse(new char[js.size() + 1]);

    memcpy(to_parse.get(), js.c_str(), js.size());
    to_parse.get()[js.size()] = 0;

    js_from.ParseInsitu(to_parse.get());

    if (js_from.IsNull())
    {
        qCritical() << "JSON document is invalid! Cannot get log message details.";
        return;
    }

    if (js_from.IsObject())
    {
        rapidjson::Value::Object jsobj(js_from.GetObject());

        int i = 0;
        for (rapidjson::Value::Object::MemberIterator it = jsobj.MemberBegin(); it != jsobj.MemberEnd(); ++it)
        {
            std::string key(it->name.GetString());

            rapidjson::StringBuffer buffer;
            rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);

            it->value.Accept(writer);
            std::string value(buffer.GetString());

            if (it->value.GetType() == rapidjson::Type::kTrueType)
                value = "True";
            if (it->value.GetType() == rapidjson::Type::kFalseType)
                value = "False";

            if (it->value.IsInt64())
            {
                long long num = it->value.GetInt64();
                value = std::to_string(num);
            }
            else
                if (it->value.IsDouble())
                {
                    double num = it->value.GetDouble();
                    value = std::to_string(num);
                }

            if (it->value.GetType() == rapidjson::Type::kStringType)
                value = it->value.GetString();

            widget->insertRow(i);
            widget->setVerticalHeaderItem(i, new QTableWidgetItem(key.c_str()));
            widget->setItem(i, 0, new QTableWidgetItem(value.c_str()));

            i++;
        }
    }
}

Message_Details_Dialog::~Message_Details_Dialog()
{
    delete ui;
}
<|endoftext|>


<commit_before>#include "testing/testing.hpp"

#include "platform/mwm_version.hpp"
#include "platform/platform.hpp"

#include "defines.hpp"

#include "coding/file_name_utils.hpp"
#include "coding/file_writer.hpp"
#include "coding/internal/file_data.hpp"

#include "base/logging.hpp"
#include "base/scope_guard.hpp"

#include "std/bind.hpp"
#include "std/initializer_list.hpp"
#include "std/set.hpp"

namespace
{
char const * TEST_FILE_NAME = "some_temporary_unit_test_file.tmp";

void CheckFilesPresence(string const & baseDir, unsigned typeMask,
                        initializer_list<pair<string, size_t>> const & files)
{
  Platform::TFilesWithType fwts;
  Platform::GetFilesByType(baseDir, typeMask, fwts);

  multiset<string> filesSet;
  for (auto const & fwt : fwts)
    filesSet.insert(fwt.first);

  for (auto const & file : files)
    TEST_EQUAL(filesSet.count(file.first), file.second, (file.first, file.second));
}
}  // namespace

UNIT_TEST(WritableDir)
{
  string const path = GetPlatform().WritableDir() + TEST_FILE_NAME;

  try
  {
    my::FileData f(path, my::FileData::OP_WRITE_TRUNCATE);
  }
  catch (Writer::OpenException const &)
  {
    LOG(LCRITICAL, ("Can't create file", path));
    return;
  }

  my::DeleteFileX(path);
}

UNIT_TEST(WritablePathForFile)
{
  Platform & pl = GetPlatform();
  string const p1 = pl.WritableDir() + TEST_FILE_NAME;
  string const p2 = pl.WritablePathForFile(TEST_FILE_NAME);
  TEST_EQUAL(p1, p2, ());
}

UNIT_TEST(GetReader)
{
  char const * NON_EXISTING_FILE = "mgbwuerhsnmbui45efhdbn34.tmp";
  char const * arr[] = {
    "resources-ldpi_legacy/symbols.sdf",
    "classificator.txt",
    "minsk-pass.mwm"
  };

  Platform & p = GetPlatform();
  for (size_t i = 0; i < ARRAY_SIZE(arr); ++i)
  {
    ReaderPtr<Reader> r = p.GetReader(arr[i]);
    TEST_GREATER(r.Size(), 0, ("File should exist!"));
  }

  bool wasException = false;
  try
  {
    ReaderPtr<Reader> r = p.GetReader(NON_EXISTING_FILE);
  }
  catch (FileAbsentException const &)
  {
    wasException = true;
  }
  TEST_EQUAL(wasException, true, ());
}

UNIT_TEST(GetFilesInDir_Smoke)
{
  Platform & pl = GetPlatform();
  Platform::FilesList files1, files2;

  string const dir = pl.ResourcesDir();

  pl.GetFilesByExt(dir, DATA_FILE_EXTENSION, files1);
  TEST_GREATER(files1.size(), 0, (dir, "folder should contain some data files"));

  pl.GetFilesByRegExp(dir, ".*\\" DATA_FILE_EXTENSION "$", files2);
  TEST_EQUAL(files1, files2, ());

  files1.clear();
  pl.GetFilesByExt(dir, ".dsa", files1);
  TEST_EQUAL(files1.size(), 0, ());
}

UNIT_TEST(DirsRoutines)
{
  Platform & platform = GetPlatform();
  string const baseDir = platform.WritableDir();
  string const testDir = my::JoinFoldersToPath(baseDir, "test-dir");
  string const testFile = my::JoinFoldersToPath(testDir, "test-file");

  TEST(!Platform::IsFileExistsByFullPath(testDir), ());
  TEST_EQUAL(platform.MkDir(testDir), Platform::ERR_OK, ());

  TEST(Platform::IsFileExistsByFullPath(testDir), ());
  TEST(Platform::IsDirectoryEmpty(testDir), ());

  {
    FileWriter writer(testFile);
  }
  TEST(!Platform::IsDirectoryEmpty(testDir), ());
  FileWriter::DeleteFileX(testFile);

  TEST_EQUAL(Platform::RmDir(testDir), Platform::ERR_OK, ());

  TEST(!Platform::IsFileExistsByFullPath(testDir), ());
}

UNIT_TEST(GetFilesByType)
{
  string const kTestDirBaseName = "test-dir";
  string const kTestFileBaseName = "test-file";

  Platform & platform = GetPlatform();
  string const baseDir = platform.WritableDir();

  string const testDir = my::JoinFoldersToPath(baseDir, kTestDirBaseName);
  TEST_EQUAL(platform.MkDir(testDir), Platform::ERR_OK, ());
  MY_SCOPE_GUARD(removeTestDir, bind(&Platform::RmDir, testDir));

  string const testFile = my::JoinFoldersToPath(baseDir, kTestFileBaseName);
  TEST(!Platform::IsFileExistsByFullPath(testFile), ());
  {
    FileWriter writer(testFile);
  }
  TEST(Platform::IsFileExistsByFullPath(testFile), ());
  MY_SCOPE_GUARD(removeTestFile, bind(FileWriter::DeleteFileX, testFile));

  CheckFilesPresence(baseDir, Platform::FILE_TYPE_DIRECTORY,
                     {{
                       kTestDirBaseName, 1 /* present */
                      },
                      {
                       kTestFileBaseName, 0 /* not present */
                      }});
  CheckFilesPresence(baseDir, Platform::FILE_TYPE_REGULAR,
                     {{
                       kTestDirBaseName, 0 /* not present */
                      },
                      {
                       kTestFileBaseName, 1 /* present */
                      }});
  CheckFilesPresence(baseDir, Platform::FILE_TYPE_DIRECTORY | Platform::FILE_TYPE_REGULAR,
                     {{
                       kTestDirBaseName, 1 /* present */
                      },
                      {
                       kTestFileBaseName, 1 /* present */
                      }});
}

UNIT_TEST(GetFileSize)
{
  Platform & pl = GetPlatform();
  uint64_t size = 123141;
  TEST(!pl.GetFileSizeByName("adsmngfuwrbfyfwe", size), ());
  TEST(!pl.IsFileExistsByFullPath("adsmngfuwrbfyfwe"), ());

  {
    FileWriter testFile(TEST_FILE_NAME);
    testFile.Write("HOHOHO", 6);
  }
  size = 0;
  TEST(Platform::GetFileSizeByFullPath(TEST_FILE_NAME, size), ());
  TEST_EQUAL(size, 6, ());

  FileWriter::DeleteFileX(TEST_FILE_NAME);

  {
    FileWriter testFile(pl.WritablePathForFile(TEST_FILE_NAME));
    testFile.Write("HOHOHO", 6);
  }
  size = 0;
  TEST(pl.GetFileSizeByName(TEST_FILE_NAME, size), ());
  TEST_EQUAL(size, 6, ());

  FileWriter::DeleteFileX(pl.WritablePathForFile(TEST_FILE_NAME));
}

UNIT_TEST(CpuCores)
{
  int const coresNum = GetPlatform().CpuCores();
  TEST_GREATER(coresNum, 0, ());
  TEST_LESS_OR_EQUAL(coresNum, 128, ());
}

UNIT_TEST(GetWritableStorageStatus)
{
  TEST_EQUAL(Platform::STORAGE_OK, GetPlatform().GetWritableStorageStatus(100000), ());
  TEST_EQUAL(Platform::NOT_ENOUGH_SPACE, GetPlatform().GetWritableStorageStatus(uint64_t(-1)), ());
}

UNIT_TEST(RmDirRecursively)
{
  Platform & platform = GetPlatform();

  string const testDir1 = my::JoinFoldersToPath(platform.WritableDir(), "test_dir1");
  TEST_EQUAL(platform.MkDir(testDir1), Platform::ERR_OK, ());
  MY_SCOPE_GUARD(removeTestDir1, bind(&Platform::RmDir, testDir1));

  string const testDir2 = my::JoinFoldersToPath(testDir1, "test_dir2");
  TEST_EQUAL(platform.MkDir(testDir2), Platform::ERR_OK, ());
  MY_SCOPE_GUARD(removeTestDir2, bind(&Platform::RmDir, testDir2));

  string const filePath = my::JoinFoldersToPath(testDir2, "test_file");
  {
    FileWriter testFile(filePath);
    testFile.Write("HOHOHO", 6);
  }
  MY_SCOPE_GUARD(removeTestFile, bind(&my::DeleteFileX, filePath));

  TEST(Platform::IsFileExistsByFullPath(filePath), ());
  TEST(Platform::IsFileExistsByFullPath(testDir1), ());
  TEST(Platform::IsFileExistsByFullPath(testDir2), ());

  TEST_EQUAL(Platform::ERR_DIRECTORY_NOT_EMPTY, Platform::RmDir(testDir1), ());

  TEST(Platform::IsFileExistsByFullPath(filePath), ());
  TEST(Platform::IsFileExistsByFullPath(testDir1), ());
  TEST(Platform::IsFileExistsByFullPath(testDir2), ());

  TEST(Platform::RmDirRecursively(testDir1), ());

  TEST(!Platform::IsFileExistsByFullPath(filePath), ());
  TEST(!Platform::IsFileExistsByFullPath(testDir1), ());
  TEST(!Platform::IsFileExistsByFullPath(testDir2), ());
}

UNIT_TEST(IsSingleMwm)
{
  TEST(version::IsSingleMwm(version::FOR_TESTING_SINGLE_MWM1), ());
  TEST(version::IsSingleMwm(version::FOR_TESTING_SINGLE_MWM_LATEST), ());
  TEST(!version::IsSingleMwm(version::FOR_TESTING_TWO_COMPONENT_MWM1), ());
  TEST(!version::IsSingleMwm(version::FOR_TESTING_TWO_COMPONENT_MWM2), ());
}
<commit_msg>[new downloader] Fix platform tests for running on device<commit_after>#include "testing/testing.hpp"

#include "platform/mwm_version.hpp"
#include "platform/platform.hpp"

#include "defines.hpp"

#include "coding/file_name_utils.hpp"
#include "coding/file_writer.hpp"
#include "coding/internal/file_data.hpp"

#include "base/logging.hpp"
#include "base/scope_guard.hpp"

#include "std/bind.hpp"
#include "std/initializer_list.hpp"
#include "std/set.hpp"

namespace
{
char const * TEST_FILE_NAME = "some_temporary_unit_test_file.tmp";

void CheckFilesPresence(string const & baseDir, unsigned typeMask,
                        initializer_list<pair<string, size_t>> const & files)
{
  Platform::TFilesWithType fwts;
  Platform::GetFilesByType(baseDir, typeMask, fwts);

  multiset<string> filesSet;
  for (auto const & fwt : fwts)
    filesSet.insert(fwt.first);

  for (auto const & file : files)
    TEST_EQUAL(filesSet.count(file.first), file.second, (file.first, file.second));
}
}  // namespace

UNIT_TEST(WritableDir)
{
  string const path = GetPlatform().WritableDir() + TEST_FILE_NAME;

  try
  {
    my::FileData f(path, my::FileData::OP_WRITE_TRUNCATE);
  }
  catch (Writer::OpenException const &)
  {
    LOG(LCRITICAL, ("Can't create file", path));
    return;
  }

  my::DeleteFileX(path);
}

UNIT_TEST(WritablePathForFile)
{
  Platform & pl = GetPlatform();
  string const p1 = pl.WritableDir() + TEST_FILE_NAME;
  string const p2 = pl.WritablePathForFile(TEST_FILE_NAME);
  TEST_EQUAL(p1, p2, ());
}

UNIT_TEST(GetReader)
{
  char const * NON_EXISTING_FILE = "mgbwuerhsnmbui45efhdbn34.tmp";
  char const * arr[] = {
    "resources-ldpi_legacy/symbols.sdf",
    "classificator.txt",
    "minsk-pass.mwm"
  };

  Platform & p = GetPlatform();
  for (size_t i = 0; i < ARRAY_SIZE(arr); ++i)
  {
    ReaderPtr<Reader> r = p.GetReader(arr[i]);
    TEST_GREATER(r.Size(), 0, ("File should exist!"));
  }

  bool wasException = false;
  try
  {
    ReaderPtr<Reader> r = p.GetReader(NON_EXISTING_FILE);
  }
  catch (FileAbsentException const &)
  {
    wasException = true;
  }
  TEST_EQUAL(wasException, true, ());
}

UNIT_TEST(GetFilesInDir_Smoke)
{
  Platform & pl = GetPlatform();
  Platform::FilesList files1, files2;

  string const dir = pl.ResourcesDir();

  pl.GetFilesByExt(dir, DATA_FILE_EXTENSION, files1);
  TEST_GREATER(files1.size(), 0, (dir, "folder should contain some data files"));

  pl.GetFilesByRegExp(dir, ".*\\" DATA_FILE_EXTENSION "$", files2);
  TEST_EQUAL(files1, files2, ());

  files1.clear();
  pl.GetFilesByExt(dir, ".dsa", files1);
  TEST_EQUAL(files1.size(), 0, ());
}

UNIT_TEST(DirsRoutines)
{
  Platform & platform = GetPlatform();
  string const baseDir = platform.WritableDir();
  string const testDir = my::JoinFoldersToPath(baseDir, "test-dir");
  string const testFile = my::JoinFoldersToPath(testDir, "test-file");

  TEST(!Platform::IsFileExistsByFullPath(testDir), ());
  TEST_EQUAL(platform.MkDir(testDir), Platform::ERR_OK, ());

  TEST(Platform::IsFileExistsByFullPath(testDir), ());
  TEST(Platform::IsDirectoryEmpty(testDir), ());

  {
    FileWriter writer(testFile);
  }
  TEST(!Platform::IsDirectoryEmpty(testDir), ());
  FileWriter::DeleteFileX(testFile);

  TEST_EQUAL(Platform::RmDir(testDir), Platform::ERR_OK, ());

  TEST(!Platform::IsFileExistsByFullPath(testDir), ());
}

UNIT_TEST(GetFilesByType)
{
  string const kTestDirBaseName = "test-dir";
  string const kTestFileBaseName = "test-file";

  Platform & platform = GetPlatform();
  string const baseDir = platform.WritableDir();

  string const testDir = my::JoinFoldersToPath(baseDir, kTestDirBaseName);
  TEST_EQUAL(platform.MkDir(testDir), Platform::ERR_OK, ());
  MY_SCOPE_GUARD(removeTestDir, bind(&Platform::RmDir, testDir));

  string const testFile = my::JoinFoldersToPath(baseDir, kTestFileBaseName);
  TEST(!Platform::IsFileExistsByFullPath(testFile), ());
  {
    FileWriter writer(testFile);
  }
  TEST(Platform::IsFileExistsByFullPath(testFile), ());
  MY_SCOPE_GUARD(removeTestFile, bind(FileWriter::DeleteFileX, testFile));

  CheckFilesPresence(baseDir, Platform::FILE_TYPE_DIRECTORY,
                     {{
                       kTestDirBaseName, 1 /* present */
                      },
                      {
                       kTestFileBaseName, 0 /* not present */
                      }});
  CheckFilesPresence(baseDir, Platform::FILE_TYPE_REGULAR,
                     {{
                       kTestDirBaseName, 0 /* not present */
                      },
                      {
                       kTestFileBaseName, 1 /* present */
                      }});
  CheckFilesPresence(baseDir, Platform::FILE_TYPE_DIRECTORY | Platform::FILE_TYPE_REGULAR,
                     {{
                       kTestDirBaseName, 1 /* present */
                      },
                      {
                       kTestFileBaseName, 1 /* present */
                      }});
}

UNIT_TEST(GetFileSize)
{
  Platform & pl = GetPlatform();
  uint64_t size = 123141;
  TEST(!pl.GetFileSizeByName("adsmngfuwrbfyfwe", size), ());
  TEST(!pl.IsFileExistsByFullPath("adsmngfuwrbfyfwe"), ());

  string const fileName = pl.WritablePathForFile(TEST_FILE_NAME);
  {
    FileWriter testFile(fileName);
    testFile.Write("HOHOHO", 6);
  }
  size = 0;
  TEST(Platform::GetFileSizeByFullPath(fileName, size), ());
  TEST_EQUAL(size, 6, ());

  FileWriter::DeleteFileX(fileName);

  {
    FileWriter testFile(pl.WritablePathForFile(TEST_FILE_NAME));
    testFile.Write("HOHOHO", 6);
  }
  size = 0;
  TEST(pl.GetFileSizeByName(TEST_FILE_NAME, size), ());
  TEST_EQUAL(size, 6, ());

  FileWriter::DeleteFileX(pl.WritablePathForFile(TEST_FILE_NAME));
}

UNIT_TEST(CpuCores)
{
  int const coresNum = GetPlatform().CpuCores();
  TEST_GREATER(coresNum, 0, ());
  TEST_LESS_OR_EQUAL(coresNum, 128, ());
}

UNIT_TEST(GetWritableStorageStatus)
{
  TEST_EQUAL(Platform::STORAGE_OK, GetPlatform().GetWritableStorageStatus(100000), ());
  TEST_EQUAL(Platform::NOT_ENOUGH_SPACE, GetPlatform().GetWritableStorageStatus(uint64_t(-1)), ());
}

UNIT_TEST(RmDirRecursively)
{
  Platform & platform = GetPlatform();

  string const testDir1 = my::JoinFoldersToPath(platform.WritableDir(), "test_dir1");
  TEST_EQUAL(platform.MkDir(testDir1), Platform::ERR_OK, ());
  MY_SCOPE_GUARD(removeTestDir1, bind(&Platform::RmDir, testDir1));

  string const testDir2 = my::JoinFoldersToPath(testDir1, "test_dir2");
  TEST_EQUAL(platform.MkDir(testDir2), Platform::ERR_OK, ());
  MY_SCOPE_GUARD(removeTestDir2, bind(&Platform::RmDir, testDir2));

  string const filePath = my::JoinFoldersToPath(testDir2, "test_file");
  {
    FileWriter testFile(filePath);
    testFile.Write("HOHOHO", 6);
  }
  MY_SCOPE_GUARD(removeTestFile, bind(&my::DeleteFileX, filePath));

  TEST(Platform::IsFileExistsByFullPath(filePath), ());
  TEST(Platform::IsFileExistsByFullPath(testDir1), ());
  TEST(Platform::IsFileExistsByFullPath(testDir2), ());

  TEST_EQUAL(Platform::ERR_DIRECTORY_NOT_EMPTY, Platform::RmDir(testDir1), ());

  TEST(Platform::IsFileExistsByFullPath(filePath), ());
  TEST(Platform::IsFileExistsByFullPath(testDir1), ());
  TEST(Platform::IsFileExistsByFullPath(testDir2), ());

  TEST(Platform::RmDirRecursively(testDir1), ());

  TEST(!Platform::IsFileExistsByFullPath(filePath), ());
  TEST(!Platform::IsFileExistsByFullPath(testDir1), ());
  TEST(!Platform::IsFileExistsByFullPath(testDir2), ());
}

UNIT_TEST(IsSingleMwm)
{
  TEST(version::IsSingleMwm(version::FOR_TESTING_SINGLE_MWM1), ());
  TEST(version::IsSingleMwm(version::FOR_TESTING_SINGLE_MWM_LATEST), ());
  TEST(!version::IsSingleMwm(version::FOR_TESTING_TWO_COMPONENT_MWM1), ());
  TEST(!version::IsSingleMwm(version::FOR_TESTING_TWO_COMPONENT_MWM2), ());
}
<|endoftext|>


<commit_before>/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * Copyright 2008 by Sun Microsystems, Inc.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * $RCSfile: querydlg.cxx,v $
 * $Revision: 1.17 $
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/

// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_dbaccess.hxx"
#ifndef DBAUI_QUERYDLG_HXX
#include "querydlg.hxx"
#endif
#ifndef _DBU_QRY_HRC_
#include "dbu_qry.hrc"
#endif
#ifndef DBAUI_QUERYDLG_HRC
#include "querydlg.hrc"
#endif
#ifndef _TOOLS_DEBUG_HXX
#include <tools/debug.hxx>
#endif
#ifndef DBAUI_QTABLECONNECTIONDATA_HXX
#include "QTableConnectionData.hxx"
#endif
#ifndef DBAUI_QUERYCONTROLLER_HXX
#include "querycontroller.hxx"
#endif
#ifndef DBAUI_QUERYTABLEVIEW_HXX
#include "QueryTableView.hxx"
#endif
#ifndef DBAUI_QUERYDESIGNVIEW_HXX
#include "QueryDesignView.hxx"
#endif
#ifndef _COM_SUN_STAR_SDBC_XDATABASEMETADATA_HPP_
#include <com/sun/star/sdbc/XDatabaseMetaData.hpp>
#endif
#ifndef DBAUI_RELATIONCONTROL_HXX
#include "RelationControl.hxx"
#endif
#ifndef _SV_MSGBOX_HXX
#include <vcl/msgbox.hxx>
#endif

using namespace dbaui;
using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::container;
using namespace ::com::sun::star::sdbc;

namespace dbaui
{
class OJoinControl  : public Window
{
public:
    FixedLine               aFL_Join;
    FixedText               aFT_Title;
    ListBox                 aLB_JoinType;
    CheckBox                m_aCBNatural;

    OJoinControl(Window* _pParent,const ResId& _rResId);
};
OJoinControl::OJoinControl(Window* _pParent,const ResId& _rResId)
    : Window(_pParent,_rResId)
    ,aFL_Join( this, ResId( FL_JOIN,*_rResId.GetResMgr() ) )
    ,aFT_Title( this, ResId(FT_LISTBOXTITLE,*_rResId.GetResMgr()) )
    ,aLB_JoinType( this, ResId(LB_JOINTYPE,*_rResId.GetResMgr()) )
    ,m_aCBNatural( this, ResId(CB_NATURAL,*_rResId.GetResMgr()) )
{
    FreeResource();
}
// -----------------------------------------------------------------------------
} // dbaui
// -----------------------------------------------------------------------------
DBG_NAME(DlgQryJoin)
DlgQryJoin::DlgQryJoin( OQueryTableView * pParent,
                       const TTableConnectionData::value_type& _pData,
                       OJoinTableView::OTableWindowMap* _pTableMap,
                       const Reference< XConnection >& _xConnection,
                       BOOL _bAllowTableSelect)
    :ModalDialog( pParent, ModuleRes(DLG_QRY_JOIN) )
    ,aML_HelpText( this, ModuleRes(ML_HELPTEXT) )
    ,aPB_OK( this, ModuleRes( PB_OK ) )
    ,aPB_CANCEL( this, ModuleRes( PB_CANCEL ) )
    ,aPB_HELP( this, ModuleRes( PB_HELP ) )
    ,m_pJoinControl( NULL )
    ,m_pTableControl( NULL )
    ,m_pTableMap(_pTableMap)
    ,m_pTableView(pParent)
    ,eJoinType(static_cast<OQueryTableConnectionData*>(_pData.get())->GetJoinType())
    ,m_pOrigConnData(_pData)
    ,m_xConnection(_xConnection)
{
    DBG_CTOR(DlgQryJoin,NULL);

    aML_HelpText.SetControlBackground( GetSettings().GetStyleSettings().GetFaceColor() );
    //////////////////////////////////////////////////////////////////////
    // Connection kopieren
    m_pConnData.reset(_pData->NewInstance());
    m_pConnData->CopyFrom(*_pData);

    m_pTableControl = new OTableListBoxControl(this,ModuleRes(WND_CONTROL),m_pTableMap,this);

    m_pJoinControl = new OJoinControl(m_pTableControl,ModuleRes(WND_JOIN_CONTROL));

    m_pJoinControl->Show();
    m_pJoinControl->m_aCBNatural.Check(static_cast<OQueryTableConnectionData*>(m_pConnData.get())->isNatural());
    m_pTableControl->Show();

    if( _bAllowTableSelect )
    {
        m_pTableControl->Init( m_pConnData );
        m_pTableControl->fillListBoxes();
    }
    else
    {
        m_pTableControl->fillAndDisable(m_pConnData);
        m_pTableControl->Init( m_pConnData );
    }

    m_pTableControl->lateUIInit(m_pJoinControl);

    sal_Bool bSupportFullJoin = sal_False;
    Reference<XDatabaseMetaData> xMeta;
    try
    {
        xMeta = m_xConnection->getMetaData();
        if ( xMeta.is() )
            bSupportFullJoin = xMeta->supportsFullOuterJoins();
    }
    catch(SQLException&)
    {
    }
    sal_Bool bSupportOuterJoin = sal_False;
    try
    {
        if ( xMeta.is() )
            bSupportOuterJoin= xMeta->supportsOuterJoins();
    }
    catch(SQLException&)
    {
    }

    setJoinType(eJoinType);

    aPB_OK.SetClickHdl( LINK(this, DlgQryJoin, OKClickHdl) );

    m_pJoinControl->aLB_JoinType.SetSelectHdl(LINK(this,DlgQryJoin,LBChangeHdl));
    m_pJoinControl->m_aCBNatural.SetToggleHdl(LINK(this,DlgQryJoin,NaturalToggleHdl));

    if ( static_cast<OQueryTableView*>(pParent)->getDesignView()->getController()->isReadOnly() )
    {
        m_pJoinControl->aLB_JoinType.Disable();
        m_pJoinControl->m_aCBNatural.Disable();
        m_pTableControl->Disable();
    }
    else
    {
        const USHORT nCount = m_pJoinControl->aLB_JoinType.GetEntryCount();
        for (USHORT i = 0; i < nCount; ++i)
        {
            const long nJoinTyp = reinterpret_cast<long>(m_pJoinControl->aLB_JoinType.GetEntryData(i));
            if ( !bSupportFullJoin && nJoinTyp == ID_FULL_JOIN )
                m_pJoinControl->aLB_JoinType.RemoveEntry(i);
            else if ( !bSupportOuterJoin && (nJoinTyp == ID_LEFT_JOIN || nJoinTyp == ID_RIGHT_JOIN) )
                m_pJoinControl->aLB_JoinType.RemoveEntry(i);
        }

        m_pTableControl->NotifyCellChange();
        m_pTableControl->enableRelation(!static_cast<OQueryTableConnectionData*>(m_pConnData.get())->isNatural() && eJoinType != CROSS_JOIN );
    }

    FreeResource();
}

//------------------------------------------------------------------------
DlgQryJoin::~DlgQryJoin()
{
    DBG_DTOR(DlgQryJoin,NULL);
    delete m_pJoinControl;
    delete m_pTableControl;
}
// -----------------------------------------------------------------------------
IMPL_LINK( DlgQryJoin, LBChangeHdl, ListBox*, /*pListBox*/ )
{
    DBG_CHKTHIS(DlgQryJoin,NULL);
    if (m_pJoinControl->aLB_JoinType.GetSelectEntryPos() == m_pJoinControl->aLB_JoinType.GetSavedValue() )
        return 1;

    m_pJoinControl->aLB_JoinType.SaveValue();
    aML_HelpText.SetText(String());

    m_pTableControl->enableRelation(true);

    const String sFirstWinName  = m_pConnData->getReferencingTable()->GetWinName();
    const String sSecondWinName = m_pConnData->getReferencedTable()->GetWinName();
    const EJoinType eOldJoinType = eJoinType;
    USHORT nResId = 0;
    const USHORT nPos = m_pJoinControl->aLB_JoinType.GetSelectEntryPos();
    const long nJoinType = reinterpret_cast<long>(m_pJoinControl->aLB_JoinType.GetEntryData(nPos));
    sal_Bool bAddHint = sal_True;
    switch ( nJoinType )
    {
        default:
        case ID_INNER_JOIN:
            nResId = STR_QUERY_INNER_JOIN;
            bAddHint = sal_False;
            eJoinType = INNER_JOIN;
            break;
        case ID_LEFT_JOIN:
            nResId = STR_QUERY_LEFTRIGHT_JOIN;
            eJoinType = LEFT_JOIN;
            break;
        case ID_RIGHT_JOIN:
            nResId = STR_QUERY_LEFTRIGHT_JOIN;
            eJoinType = RIGHT_JOIN;
            break;
        case ID_FULL_JOIN:
            nResId = STR_QUERY_FULL_JOIN;
            eJoinType = FULL_JOIN;
            break;
        case ID_CROSS_JOIN:
            {
                nResId = STR_QUERY_CROSS_JOIN;
                eJoinType = CROSS_JOIN;

                m_pConnData->ResetConnLines();
                m_pTableControl->lateInit();
                m_pJoinControl->m_aCBNatural.Check(FALSE);
                m_pTableControl->enableRelation(false);
                ::rtl::OUString sEmpty;
                m_pConnData->AppendConnLine(sEmpty,sEmpty);
                aPB_OK.Enable(TRUE);
            }
            break;
    }

    m_pJoinControl->m_aCBNatural.Enable(eJoinType != CROSS_JOIN);

    if ( eJoinType != eOldJoinType && eOldJoinType == CROSS_JOIN )
    {
        m_pConnData->ResetConnLines();
    }
    if ( eJoinType != CROSS_JOIN )
    {
        m_pTableControl->NotifyCellChange();
        NaturalToggleHdl(&m_pJoinControl->m_aCBNatural);
    }

    m_pTableControl->Invalidate();

    String sHelpText = String( ModuleRes( nResId ) );
    if( nPos )
    {
        sHelpText.SearchAndReplace( String( RTL_CONSTASCII_STRINGPARAM( "%1" ) ), sFirstWinName );
        sHelpText.SearchAndReplace( String( RTL_CONSTASCII_STRINGPARAM( "%2" ) ), sSecondWinName );
    }
    if ( bAddHint )
    {
        sHelpText += String( RTL_CONSTASCII_STRINGPARAM( "\n" ) );
        sHelpText += String( ModuleRes( STR_JOIN_TYPE_HINT ) );
    }

    aML_HelpText.SetText( sHelpText );
    return 1;
}
// -----------------------------------------------------------------------------

IMPL_LINK( DlgQryJoin, OKClickHdl, Button*, /*pButton*/ )
{
    DBG_CHKTHIS(DlgQryJoin,NULL);

    m_pConnData->Update();
    m_pOrigConnData->CopyFrom( *m_pConnData );

    EndDialog(RET_OK);
    return 1;
}
// -----------------------------------------------------------------------------

IMPL_LINK( DlgQryJoin, NaturalToggleHdl, CheckBox*, /*pButton*/ )
{
    DBG_CHKTHIS(DlgQryJoin,NULL);
    BOOL bChecked = m_pJoinControl->m_aCBNatural.IsChecked();
    static_cast<OQueryTableConnectionData*>(m_pConnData.get())->setNatural(bChecked);
    m_pTableControl->enableRelation(!bChecked);
    if ( bChecked )
    {
        m_pConnData->ResetConnLines();
        try
        {
            Reference<XNameAccess> xReferencedTableColumns(m_pConnData->getReferencedTable()->getColumns());
            Sequence< ::rtl::OUString> aSeq = m_pConnData->getReferencingTable()->getColumns()->getElementNames();
            const ::rtl::OUString* pIter = aSeq.getConstArray();
            const ::rtl::OUString* pEnd   = pIter + aSeq.getLength();
            for(;pIter != pEnd;++pIter)
            {
                if ( xReferencedTableColumns->hasByName(*pIter) )
                    m_pConnData->AppendConnLine(*pIter,*pIter);
            }
        }
        catch(const Exception&)
        {
            OSL_ENSURE(0,"Exception caught while asking for column names in case of a natural join.");
        }
        m_pTableControl->NotifyCellChange();
        m_pTableControl->Invalidate();
    }

    return 1;
}
// -----------------------------------------------------------------------------
TTableConnectionData::value_type DlgQryJoin::getConnectionData() const
{
    return m_pConnData;
}
// -----------------------------------------------------------------------------
void DlgQryJoin::setValid(sal_Bool _bValid)
{
    //LBChangeHdl(&aLB_JoinType);

    aPB_OK.Enable(_bValid || eJoinType == CROSS_JOIN );
}
// -----------------------------------------------------------------------------
void DlgQryJoin::notifyConnectionChange( )
{
    setJoinType( static_cast<OQueryTableConnectionData*>(m_pConnData.get())->GetJoinType() );
    m_pJoinControl->m_aCBNatural.Check(static_cast<OQueryTableConnectionData*>(m_pConnData.get())->isNatural());
    NaturalToggleHdl(&m_pJoinControl->m_aCBNatural);
}
// -----------------------------------------------------------------------------
void DlgQryJoin::setJoinType(EJoinType _eNewJoinType)
{
    eJoinType = _eNewJoinType;
    m_pJoinControl->m_aCBNatural.Enable(eJoinType != CROSS_JOIN);

    long nJoinType = 0;
    switch ( eJoinType )
    {
        default:
        case INNER_JOIN:
            nJoinType = ID_INNER_JOIN;
            break;
        case LEFT_JOIN:
            nJoinType = ID_LEFT_JOIN;
            break;
        case RIGHT_JOIN:
            nJoinType = ID_RIGHT_JOIN;
            break;
        case FULL_JOIN:
            nJoinType = ID_FULL_JOIN;
            break;
        case CROSS_JOIN:
            nJoinType = ID_CROSS_JOIN;
            break;
    }

    const USHORT nCount = m_pJoinControl->aLB_JoinType.GetEntryCount();
    for (USHORT i = 0; i < nCount; ++i)
    {
        if ( nJoinType == reinterpret_cast<long>(m_pJoinControl->aLB_JoinType.GetEntryData(i)) )
        {
            m_pJoinControl->aLB_JoinType.SelectEntryPos(i);
            break;
        }
    }

    LBChangeHdl(&m_pJoinControl->aLB_JoinType);
}
// -----------------------------------------------------------------------------

<commit_msg>INTEGRATION: CWS dba30b (1.16.78); FILE MERGED 2008/04/15 22:14:25 fs 1.16.78.2: RESYNC: (1.16-1.17); FILE MERGED 2008/03/16 14:07:58 fs 1.16.78.1: some exception handling re-factoring<commit_after>/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * Copyright 2008 by Sun Microsystems, Inc.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * $RCSfile: querydlg.cxx,v $
 * $Revision: 1.18 $
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/

// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_dbaccess.hxx"
#ifndef DBAUI_QUERYDLG_HXX
#include "querydlg.hxx"
#endif
#ifndef _DBU_QRY_HRC_
#include "dbu_qry.hrc"
#endif
#ifndef DBAUI_QUERYDLG_HRC
#include "querydlg.hrc"
#endif
#ifndef _TOOLS_DEBUG_HXX
#include <tools/debug.hxx>
#endif
#ifndef TOOLS_DIAGNOSE_EX_H
#include <tools/diagnose_ex.h>
#endif
#ifndef DBAUI_QTABLECONNECTIONDATA_HXX
#include "QTableConnectionData.hxx"
#endif
#ifndef DBAUI_QUERYCONTROLLER_HXX
#include "querycontroller.hxx"
#endif
#ifndef DBAUI_QUERYTABLEVIEW_HXX
#include "QueryTableView.hxx"
#endif
#ifndef DBAUI_QUERYDESIGNVIEW_HXX
#include "QueryDesignView.hxx"
#endif
#ifndef _COM_SUN_STAR_SDBC_XDATABASEMETADATA_HPP_
#include <com/sun/star/sdbc/XDatabaseMetaData.hpp>
#endif
#ifndef DBAUI_RELATIONCONTROL_HXX
#include "RelationControl.hxx"
#endif
#ifndef _SV_MSGBOX_HXX
#include <vcl/msgbox.hxx>
#endif

using namespace dbaui;
using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::container;
using namespace ::com::sun::star::sdbc;

namespace dbaui
{
class OJoinControl  : public Window
{
public:
    FixedLine               aFL_Join;
    FixedText               aFT_Title;
    ListBox                 aLB_JoinType;
    CheckBox                m_aCBNatural;

    OJoinControl(Window* _pParent,const ResId& _rResId);
};
OJoinControl::OJoinControl(Window* _pParent,const ResId& _rResId)
    : Window(_pParent,_rResId)
    ,aFL_Join( this, ResId( FL_JOIN,*_rResId.GetResMgr() ) )
    ,aFT_Title( this, ResId(FT_LISTBOXTITLE,*_rResId.GetResMgr()) )
    ,aLB_JoinType( this, ResId(LB_JOINTYPE,*_rResId.GetResMgr()) )
    ,m_aCBNatural( this, ResId(CB_NATURAL,*_rResId.GetResMgr()) )
{
    FreeResource();
}
// -----------------------------------------------------------------------------
} // dbaui
// -----------------------------------------------------------------------------
DBG_NAME(DlgQryJoin)
DlgQryJoin::DlgQryJoin( OQueryTableView * pParent,
                       const TTableConnectionData::value_type& _pData,
                       OJoinTableView::OTableWindowMap* _pTableMap,
                       const Reference< XConnection >& _xConnection,
                       BOOL _bAllowTableSelect)
    :ModalDialog( pParent, ModuleRes(DLG_QRY_JOIN) )
    ,aML_HelpText( this, ModuleRes(ML_HELPTEXT) )
    ,aPB_OK( this, ModuleRes( PB_OK ) )
    ,aPB_CANCEL( this, ModuleRes( PB_CANCEL ) )
    ,aPB_HELP( this, ModuleRes( PB_HELP ) )
    ,m_pJoinControl( NULL )
    ,m_pTableControl( NULL )
    ,m_pTableMap(_pTableMap)
    ,m_pTableView(pParent)
    ,eJoinType(static_cast<OQueryTableConnectionData*>(_pData.get())->GetJoinType())
    ,m_pOrigConnData(_pData)
    ,m_xConnection(_xConnection)
{
    DBG_CTOR(DlgQryJoin,NULL);

    aML_HelpText.SetControlBackground( GetSettings().GetStyleSettings().GetFaceColor() );
    //////////////////////////////////////////////////////////////////////
    // Connection kopieren
    m_pConnData.reset(_pData->NewInstance());
    m_pConnData->CopyFrom(*_pData);

    m_pTableControl = new OTableListBoxControl(this,ModuleRes(WND_CONTROL),m_pTableMap,this);

    m_pJoinControl = new OJoinControl(m_pTableControl,ModuleRes(WND_JOIN_CONTROL));

    m_pJoinControl->Show();
    m_pJoinControl->m_aCBNatural.Check(static_cast<OQueryTableConnectionData*>(m_pConnData.get())->isNatural());
    m_pTableControl->Show();

    if( _bAllowTableSelect )
    {
        m_pTableControl->Init( m_pConnData );
        m_pTableControl->fillListBoxes();
    }
    else
    {
        m_pTableControl->fillAndDisable(m_pConnData);
        m_pTableControl->Init( m_pConnData );
    }

    m_pTableControl->lateUIInit(m_pJoinControl);

    sal_Bool bSupportFullJoin = sal_False;
    Reference<XDatabaseMetaData> xMeta;
    try
    {
        xMeta = m_xConnection->getMetaData();
        if ( xMeta.is() )
            bSupportFullJoin = xMeta->supportsFullOuterJoins();
    }
    catch(SQLException&)
    {
    }
    sal_Bool bSupportOuterJoin = sal_False;
    try
    {
        if ( xMeta.is() )
            bSupportOuterJoin= xMeta->supportsOuterJoins();
    }
    catch(SQLException&)
    {
    }

    setJoinType(eJoinType);

    aPB_OK.SetClickHdl( LINK(this, DlgQryJoin, OKClickHdl) );

    m_pJoinControl->aLB_JoinType.SetSelectHdl(LINK(this,DlgQryJoin,LBChangeHdl));
    m_pJoinControl->m_aCBNatural.SetToggleHdl(LINK(this,DlgQryJoin,NaturalToggleHdl));

    if ( static_cast<OQueryTableView*>(pParent)->getDesignView()->getController()->isReadOnly() )
    {
        m_pJoinControl->aLB_JoinType.Disable();
        m_pJoinControl->m_aCBNatural.Disable();
        m_pTableControl->Disable();
    }
    else
    {
        const USHORT nCount = m_pJoinControl->aLB_JoinType.GetEntryCount();
        for (USHORT i = 0; i < nCount; ++i)
        {
            const long nJoinTyp = reinterpret_cast<long>(m_pJoinControl->aLB_JoinType.GetEntryData(i));
            if ( !bSupportFullJoin && nJoinTyp == ID_FULL_JOIN )
                m_pJoinControl->aLB_JoinType.RemoveEntry(i);
            else if ( !bSupportOuterJoin && (nJoinTyp == ID_LEFT_JOIN || nJoinTyp == ID_RIGHT_JOIN) )
                m_pJoinControl->aLB_JoinType.RemoveEntry(i);
        }

        m_pTableControl->NotifyCellChange();
        m_pTableControl->enableRelation(!static_cast<OQueryTableConnectionData*>(m_pConnData.get())->isNatural() && eJoinType != CROSS_JOIN );
    }

    FreeResource();
}

//------------------------------------------------------------------------
DlgQryJoin::~DlgQryJoin()
{
    DBG_DTOR(DlgQryJoin,NULL);
    delete m_pJoinControl;
    delete m_pTableControl;
}
// -----------------------------------------------------------------------------
IMPL_LINK( DlgQryJoin, LBChangeHdl, ListBox*, /*pListBox*/ )
{
    DBG_CHKTHIS(DlgQryJoin,NULL);
    if (m_pJoinControl->aLB_JoinType.GetSelectEntryPos() == m_pJoinControl->aLB_JoinType.GetSavedValue() )
        return 1;

    m_pJoinControl->aLB_JoinType.SaveValue();
    aML_HelpText.SetText(String());

    m_pTableControl->enableRelation(true);

    const String sFirstWinName  = m_pConnData->getReferencingTable()->GetWinName();
    const String sSecondWinName = m_pConnData->getReferencedTable()->GetWinName();
    const EJoinType eOldJoinType = eJoinType;
    USHORT nResId = 0;
    const USHORT nPos = m_pJoinControl->aLB_JoinType.GetSelectEntryPos();
    const long nJoinType = reinterpret_cast<long>(m_pJoinControl->aLB_JoinType.GetEntryData(nPos));
    sal_Bool bAddHint = sal_True;
    switch ( nJoinType )
    {
        default:
        case ID_INNER_JOIN:
            nResId = STR_QUERY_INNER_JOIN;
            bAddHint = sal_False;
            eJoinType = INNER_JOIN;
            break;
        case ID_LEFT_JOIN:
            nResId = STR_QUERY_LEFTRIGHT_JOIN;
            eJoinType = LEFT_JOIN;
            break;
        case ID_RIGHT_JOIN:
            nResId = STR_QUERY_LEFTRIGHT_JOIN;
            eJoinType = RIGHT_JOIN;
            break;
        case ID_FULL_JOIN:
            nResId = STR_QUERY_FULL_JOIN;
            eJoinType = FULL_JOIN;
            break;
        case ID_CROSS_JOIN:
            {
                nResId = STR_QUERY_CROSS_JOIN;
                eJoinType = CROSS_JOIN;

                m_pConnData->ResetConnLines();
                m_pTableControl->lateInit();
                m_pJoinControl->m_aCBNatural.Check(FALSE);
                m_pTableControl->enableRelation(false);
                ::rtl::OUString sEmpty;
                m_pConnData->AppendConnLine(sEmpty,sEmpty);
                aPB_OK.Enable(TRUE);
            }
            break;
    }

    m_pJoinControl->m_aCBNatural.Enable(eJoinType != CROSS_JOIN);

    if ( eJoinType != eOldJoinType && eOldJoinType == CROSS_JOIN )
    {
        m_pConnData->ResetConnLines();
    }
    if ( eJoinType != CROSS_JOIN )
    {
        m_pTableControl->NotifyCellChange();
        NaturalToggleHdl(&m_pJoinControl->m_aCBNatural);
    }

    m_pTableControl->Invalidate();

    String sHelpText = String( ModuleRes( nResId ) );
    if( nPos )
    {
        sHelpText.SearchAndReplace( String( RTL_CONSTASCII_STRINGPARAM( "%1" ) ), sFirstWinName );
        sHelpText.SearchAndReplace( String( RTL_CONSTASCII_STRINGPARAM( "%2" ) ), sSecondWinName );
    }
    if ( bAddHint )
    {
        sHelpText += String( RTL_CONSTASCII_STRINGPARAM( "\n" ) );
        sHelpText += String( ModuleRes( STR_JOIN_TYPE_HINT ) );
    }

    aML_HelpText.SetText( sHelpText );
    return 1;
}
// -----------------------------------------------------------------------------

IMPL_LINK( DlgQryJoin, OKClickHdl, Button*, /*pButton*/ )
{
    DBG_CHKTHIS(DlgQryJoin,NULL);

    m_pConnData->Update();
    m_pOrigConnData->CopyFrom( *m_pConnData );

    EndDialog(RET_OK);
    return 1;
}
// -----------------------------------------------------------------------------

IMPL_LINK( DlgQryJoin, NaturalToggleHdl, CheckBox*, /*pButton*/ )
{
    DBG_CHKTHIS(DlgQryJoin,NULL);
    BOOL bChecked = m_pJoinControl->m_aCBNatural.IsChecked();
    static_cast<OQueryTableConnectionData*>(m_pConnData.get())->setNatural(bChecked);
    m_pTableControl->enableRelation(!bChecked);
    if ( bChecked )
    {
        m_pConnData->ResetConnLines();
        try
        {
            Reference<XNameAccess> xReferencedTableColumns(m_pConnData->getReferencedTable()->getColumns());
            Sequence< ::rtl::OUString> aSeq = m_pConnData->getReferencingTable()->getColumns()->getElementNames();
            const ::rtl::OUString* pIter = aSeq.getConstArray();
            const ::rtl::OUString* pEnd   = pIter + aSeq.getLength();
            for(;pIter != pEnd;++pIter)
            {
                if ( xReferencedTableColumns->hasByName(*pIter) )
                    m_pConnData->AppendConnLine(*pIter,*pIter);
            }
        }
        catch( const Exception& )
        {
            DBG_UNHANDLED_EXCEPTION();
        }
        m_pTableControl->NotifyCellChange();
        m_pTableControl->Invalidate();
    }

    return 1;
}
// -----------------------------------------------------------------------------
TTableConnectionData::value_type DlgQryJoin::getConnectionData() const
{
    return m_pConnData;
}
// -----------------------------------------------------------------------------
void DlgQryJoin::setValid(sal_Bool _bValid)
{
    //LBChangeHdl(&aLB_JoinType);

    aPB_OK.Enable(_bValid || eJoinType == CROSS_JOIN );
}
// -----------------------------------------------------------------------------
void DlgQryJoin::notifyConnectionChange( )
{
    setJoinType( static_cast<OQueryTableConnectionData*>(m_pConnData.get())->GetJoinType() );
    m_pJoinControl->m_aCBNatural.Check(static_cast<OQueryTableConnectionData*>(m_pConnData.get())->isNatural());
    NaturalToggleHdl(&m_pJoinControl->m_aCBNatural);
}
// -----------------------------------------------------------------------------
void DlgQryJoin::setJoinType(EJoinType _eNewJoinType)
{
    eJoinType = _eNewJoinType;
    m_pJoinControl->m_aCBNatural.Enable(eJoinType != CROSS_JOIN);

    long nJoinType = 0;
    switch ( eJoinType )
    {
        default:
        case INNER_JOIN:
            nJoinType = ID_INNER_JOIN;
            break;
        case LEFT_JOIN:
            nJoinType = ID_LEFT_JOIN;
            break;
        case RIGHT_JOIN:
            nJoinType = ID_RIGHT_JOIN;
            break;
        case FULL_JOIN:
            nJoinType = ID_FULL_JOIN;
            break;
        case CROSS_JOIN:
            nJoinType = ID_CROSS_JOIN;
            break;
    }

    const USHORT nCount = m_pJoinControl->aLB_JoinType.GetEntryCount();
    for (USHORT i = 0; i < nCount; ++i)
    {
        if ( nJoinType == reinterpret_cast<long>(m_pJoinControl->aLB_JoinType.GetEntryData(i)) )
        {
            m_pJoinControl->aLB_JoinType.SelectEntryPos(i);
            break;
        }
    }

    LBChangeHdl(&m_pJoinControl->aLB_JoinType);
}
// -----------------------------------------------------------------------------

<|endoftext|>


<commit_before>/*
 * Copyright (C) 2020 ScyllaDB
 */

/*
 * This file is part of Scylla.
 *
 * Scylla is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Scylla 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 Scylla.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <algorithm>
#include <limits>
#include <ostream>
#include <utility>

#include "dht/token.hh"
#include "dht/token-sharding.hh"
#include "dht/i_partitioner.hh"

namespace dht {

using uint128_t = unsigned __int128;

inline int64_t long_token(const token& t) {
    if (t.is_minimum() || t.is_maximum()) {
        return std::numeric_limits<int64_t>::min();
    }

    return t._data;
}

static const token min_token{ token::kind::before_all_keys, 0 };
static const token max_token{ token::kind::after_all_keys, 0 };

const token&
minimum_token() noexcept {
    return min_token;
}

const token&
maximum_token() noexcept {
    return max_token;
}

std::strong_ordering tri_compare(const token& t1, const token& t2) {
    if (t1._kind < t2._kind) {
            return std::strong_ordering::less;
    } else if (t1._kind > t2._kind) {
            return std::strong_ordering::greater;
    } else if (t1._kind == token_kind::key) {
        return tri_compare_raw(long_token(t1), long_token(t2));
    }
    return std::strong_ordering::equal;
}

std::ostream& operator<<(std::ostream& out, const token& t) {
    if (t._kind == token::kind::after_all_keys) {
        out << "maximum token";
    } else if (t._kind == token::kind::before_all_keys) {
        out << "minimum token";
    } else {
        out << t.to_sstring();
    }
    return out;
}

sstring token::to_sstring() const {
    return seastar::to_sstring<sstring>(long_token(*this));
}

// Assuming that x>=y, return the positive difference x-y.
// The return type is an unsigned type, as the difference may overflow
// a signed type (e.g., consider very positive x and very negative y).
template <typename T>
static std::make_unsigned_t<T> positive_subtract(T x, T y) {
        return std::make_unsigned_t<T>(x) - std::make_unsigned_t<T>(y);
}

token token::midpoint(const token& t1, const token& t2) {
    auto l1 = long_token(t1);
    auto l2 = long_token(t2);
    int64_t mid;
    if (l1 <= l2) {
        // To find the midpoint, we cannot use the trivial formula (l1+l2)/2
        // because the addition can overflow the integer. To avoid this
        // overflow, we first notice that the above formula is equivalent to
        // l1 + (l2-l1)/2. Now, "l2-l1" can still overflow a signed integer
        // (e.g., think of a very positive l2 and very negative l1), but
        // because l1 <= l2 in this branch, we note that l2-l1 is positive
        // and fits an *unsigned* int's range. So,
        mid = l1 + positive_subtract(l2, l1)/2;
    } else {
        // When l2 < l1, we need to switch l1 and and l2 in the above
        // formula, because now l1 - l2 is positive.
        // Additionally, we consider this case is a "wrap around", so we need
        // to behave as if l2 + 2^64 was meant instead of l2, i.e., add 2^63
        // to the average.
        mid = l2 + positive_subtract(l1, l2)/2 + 0x8000'0000'0000'0000;
    }
    return token{kind::key, mid};
}

token token::get_random_token() {
    static thread_local std::default_random_engine re{std::random_device{}()};
    // std::numeric_limits<int64_t>::min() value is reserved and shouldn't
    // be used for regular tokens.
    static thread_local std::uniform_int_distribution<int64_t> dist(
            std::numeric_limits<int64_t>::min() + 1);
    return token(kind::key, dist(re));
}

token token::from_sstring(const sstring& t) {
    auto lp = boost::lexical_cast<long>(t);
    if (lp == std::numeric_limits<long>::min()) {
        return minimum_token();
    } else {
        return token(kind::key, uint64_t(lp));
    }
}

token token::from_bytes(bytes_view bytes) {
    if (bytes.size() != sizeof(int64_t)) {
        throw runtime_exception(format("Invalid token. Should have size {:d}, has size {:d}\n", sizeof(int64_t), bytes.size()));
    }

    auto tok = net::ntoh(read_unaligned<int64_t>(bytes.begin()));
    if (tok == std::numeric_limits<int64_t>::min()) {
        return minimum_token();
    } else {
        return dht::token(dht::token::kind::key, tok);
    }
}

static float ratio_helper(int64_t a, int64_t b) {

    uint64_t val = (a > b)? static_cast<uint64_t>(a) - static_cast<uint64_t>(b) : (static_cast<uint64_t>(a) - static_cast<uint64_t>(b) - 1);
    return val/(float)std::numeric_limits<uint64_t>::max();
}

std::map<token, float>
token::describe_ownership(const std::vector<token>& sorted_tokens) {
    std::map<token, float> ownerships;
    auto i = sorted_tokens.begin();

    // 0-case
    if (i == sorted_tokens.end()) {
        throw runtime_exception("No nodes present in the cluster. Has this node finished starting up?");
    }
    // 1-case
    if (sorted_tokens.size() == 1) {
        ownerships[sorted_tokens[0]] = 1.0;
    // n-case
    } else {
        const token& start = sorted_tokens[0];

        int64_t ti = long_token(start);  // The first token and its value
        int64_t start_long = ti;
        int64_t tim1 = ti; // The last token and its value (after loop)
        for (i++; i != sorted_tokens.end(); i++) {
            ti = long_token(*i); // The next token and its value
            ownerships[*i]= ratio_helper(ti, tim1);  // save (T(i) -> %age)
            tim1 = ti;
        }

        // The start token's range extends backward to the last token, which is why both were saved above.
        ownerships[start] = ratio_helper(start_long, ti);
    }

    return ownerships;
}

data_type
token::get_token_validator() {
    return long_type;
}

static uint64_t unbias(const token& t) {
    return uint64_t(long_token(t)) + uint64_t(std::numeric_limits<int64_t>::min());
}

static token bias(uint64_t n) {
    return token(token::kind::key, n - uint64_t(std::numeric_limits<int64_t>::min()));
}

inline
unsigned
zero_based_shard_of(uint64_t token, unsigned shards, unsigned sharding_ignore_msb_bits) {
    // This is the master function, the inverses have to match it wrt. rounding errors.
    token <<= sharding_ignore_msb_bits;
    // Treat "token" as a fraction in the interval [0, 1); compute:
    //    shard = floor((0.token) * shards)
    return (uint128_t(token) * shards) >> 64;
}

std::vector<uint64_t>
init_zero_based_shard_start(unsigned shards, unsigned sharding_ignore_msb_bits) {
    // computes the inverse of zero_based_shard_of(). ret[s] will return the smallest token that belongs to s
    if (shards == 1) {
        // Avoid the while loops below getting confused finding the "edge" between two nonexistent shards
        return std::vector<uint64_t>(1, uint64_t(0));
    }
    auto ret = std::vector<uint64_t>(shards);
    for (auto s : boost::irange<unsigned>(0, shards)) {
        uint64_t token = (uint128_t(s) << 64) / shards;
        token >>= sharding_ignore_msb_bits;   // leftmost bits are ignored by zero_based_shard_of
        // token is the start of the next shard, and can be slightly before due to rounding errors; adjust
        while (zero_based_shard_of(token, shards, sharding_ignore_msb_bits) != s) {
            ++token;
        }
        ret[s] = token;
    }
    return ret;
}

unsigned
shard_of(unsigned shard_count, unsigned sharding_ignore_msb_bits, const token& t) {
    switch (t._kind) {
        case token::kind::before_all_keys:
            return token::shard_of_minimum_token();
        case token::kind::after_all_keys:
            return shard_count - 1;
        case token::kind::key:
            uint64_t adjusted = unbias(t);
            return zero_based_shard_of(adjusted, shard_count, sharding_ignore_msb_bits);
    }
    abort();
}

token
token_for_next_shard(const std::vector<uint64_t>& shard_start, unsigned shard_count, unsigned sharding_ignore_msb_bits, const token& t, shard_id shard, unsigned spans) {
    uint64_t n = 0;
    switch (t._kind) {
        case token::kind::before_all_keys:
            break;
        case token::kind::after_all_keys:
            return maximum_token();
        case token::kind::key:
            n = unbias(t);
            break;
    }
    auto s = zero_based_shard_of(n, shard_count, sharding_ignore_msb_bits);

    if (!sharding_ignore_msb_bits) {
        // This ought to be the same as the else branch, but avoids shifts by 64
        n = shard_start[shard];
        if (spans > 1 || shard <= s) {
            return maximum_token();
        }
    } else {
        auto left_part = n >> (64 - sharding_ignore_msb_bits);
        left_part += spans - unsigned(shard > s);
        if (left_part >= (1u << sharding_ignore_msb_bits)) {
            return maximum_token();
        }
        left_part <<= (64 - sharding_ignore_msb_bits);
        auto right_part = shard_start[shard];
        n = left_part | right_part;
    }
    return bias(n);
}

int64_t token::to_int64(token t) {
    return long_token(t);
}

dht::token token::from_int64(int64_t i) {
    return {kind::key, i};
}

static
dht::token find_first_token_for_shard_in_not_wrap_around_range(const dht::sharder& sharder, dht::token start, dht::token end, size_t shard_idx) {
    // Invariant start < end
    // It is guaranteed that start is not MAX_INT64 because end is greater
    auto t = dht::token::from_int64(dht::token::to_int64(start) + 1);
    if (sharder.shard_of(t) != shard_idx) {
        t = sharder.token_for_next_shard(t, shard_idx);
    }
    return std::min(t, end);
}

dht::token find_first_token_for_shard(
        const dht::sharder& sharder, dht::token start, dht::token end, size_t shard_idx) {
    if (start < end) { // Not a wrap around token range
        return find_first_token_for_shard_in_not_wrap_around_range(sharder, start, end, shard_idx);
    } else { // A wrap around token range
        dht::token t;
        if (dht::token::to_int64(start) != std::numeric_limits<int64_t>::max()) {
            t = find_first_token_for_shard_in_not_wrap_around_range(sharder, start, dht::maximum_token(), shard_idx);
            if (!t.is_maximum()) {
                // This means we have found a token for shard shard_idx before 2^63
                return t;
            }
        }
        // No token owned by shard shard_idx was found in (start, 2^63 - 1]
        // so we have to search in (-2^63, end]
        return find_first_token_for_shard_in_not_wrap_around_range(
                sharder, dht::minimum_token(), end, shard_idx);
    }
}

} // namespace dht
<commit_msg>dht: token: simplify midpoint()<commit_after>/*
 * Copyright (C) 2020 ScyllaDB
 */

/*
 * This file is part of Scylla.
 *
 * Scylla is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Scylla 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 Scylla.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <algorithm>
#include <limits>
#include <ostream>
#include <utility>

#include "dht/token.hh"
#include "dht/token-sharding.hh"
#include "dht/i_partitioner.hh"

namespace dht {

using uint128_t = unsigned __int128;

inline int64_t long_token(const token& t) {
    if (t.is_minimum() || t.is_maximum()) {
        return std::numeric_limits<int64_t>::min();
    }

    return t._data;
}

static const token min_token{ token::kind::before_all_keys, 0 };
static const token max_token{ token::kind::after_all_keys, 0 };

const token&
minimum_token() noexcept {
    return min_token;
}

const token&
maximum_token() noexcept {
    return max_token;
}

std::strong_ordering tri_compare(const token& t1, const token& t2) {
    if (t1._kind < t2._kind) {
            return std::strong_ordering::less;
    } else if (t1._kind > t2._kind) {
            return std::strong_ordering::greater;
    } else if (t1._kind == token_kind::key) {
        return tri_compare_raw(long_token(t1), long_token(t2));
    }
    return std::strong_ordering::equal;
}

std::ostream& operator<<(std::ostream& out, const token& t) {
    if (t._kind == token::kind::after_all_keys) {
        out << "maximum token";
    } else if (t._kind == token::kind::before_all_keys) {
        out << "minimum token";
    } else {
        out << t.to_sstring();
    }
    return out;
}

sstring token::to_sstring() const {
    return seastar::to_sstring<sstring>(long_token(*this));
}

token token::midpoint(const token& t1, const token& t2) {
    uint64_t l1 = long_token(t1);
    uint64_t l2 = long_token(t2);
    int64_t mid = l1 + (l2 - l1)/2;
    return token{kind::key, mid};
}

token token::get_random_token() {
    static thread_local std::default_random_engine re{std::random_device{}()};
    // std::numeric_limits<int64_t>::min() value is reserved and shouldn't
    // be used for regular tokens.
    static thread_local std::uniform_int_distribution<int64_t> dist(
            std::numeric_limits<int64_t>::min() + 1);
    return token(kind::key, dist(re));
}

token token::from_sstring(const sstring& t) {
    auto lp = boost::lexical_cast<long>(t);
    if (lp == std::numeric_limits<long>::min()) {
        return minimum_token();
    } else {
        return token(kind::key, uint64_t(lp));
    }
}

token token::from_bytes(bytes_view bytes) {
    if (bytes.size() != sizeof(int64_t)) {
        throw runtime_exception(format("Invalid token. Should have size {:d}, has size {:d}\n", sizeof(int64_t), bytes.size()));
    }

    auto tok = net::ntoh(read_unaligned<int64_t>(bytes.begin()));
    if (tok == std::numeric_limits<int64_t>::min()) {
        return minimum_token();
    } else {
        return dht::token(dht::token::kind::key, tok);
    }
}

static float ratio_helper(int64_t a, int64_t b) {

    uint64_t val = (a > b)? static_cast<uint64_t>(a) - static_cast<uint64_t>(b) : (static_cast<uint64_t>(a) - static_cast<uint64_t>(b) - 1);
    return val/(float)std::numeric_limits<uint64_t>::max();
}

std::map<token, float>
token::describe_ownership(const std::vector<token>& sorted_tokens) {
    std::map<token, float> ownerships;
    auto i = sorted_tokens.begin();

    // 0-case
    if (i == sorted_tokens.end()) {
        throw runtime_exception("No nodes present in the cluster. Has this node finished starting up?");
    }
    // 1-case
    if (sorted_tokens.size() == 1) {
        ownerships[sorted_tokens[0]] = 1.0;
    // n-case
    } else {
        const token& start = sorted_tokens[0];

        int64_t ti = long_token(start);  // The first token and its value
        int64_t start_long = ti;
        int64_t tim1 = ti; // The last token and its value (after loop)
        for (i++; i != sorted_tokens.end(); i++) {
            ti = long_token(*i); // The next token and its value
            ownerships[*i]= ratio_helper(ti, tim1);  // save (T(i) -> %age)
            tim1 = ti;
        }

        // The start token's range extends backward to the last token, which is why both were saved above.
        ownerships[start] = ratio_helper(start_long, ti);
    }

    return ownerships;
}

data_type
token::get_token_validator() {
    return long_type;
}

static uint64_t unbias(const token& t) {
    return uint64_t(long_token(t)) + uint64_t(std::numeric_limits<int64_t>::min());
}

static token bias(uint64_t n) {
    return token(token::kind::key, n - uint64_t(std::numeric_limits<int64_t>::min()));
}

inline
unsigned
zero_based_shard_of(uint64_t token, unsigned shards, unsigned sharding_ignore_msb_bits) {
    // This is the master function, the inverses have to match it wrt. rounding errors.
    token <<= sharding_ignore_msb_bits;
    // Treat "token" as a fraction in the interval [0, 1); compute:
    //    shard = floor((0.token) * shards)
    return (uint128_t(token) * shards) >> 64;
}

std::vector<uint64_t>
init_zero_based_shard_start(unsigned shards, unsigned sharding_ignore_msb_bits) {
    // computes the inverse of zero_based_shard_of(). ret[s] will return the smallest token that belongs to s
    if (shards == 1) {
        // Avoid the while loops below getting confused finding the "edge" between two nonexistent shards
        return std::vector<uint64_t>(1, uint64_t(0));
    }
    auto ret = std::vector<uint64_t>(shards);
    for (auto s : boost::irange<unsigned>(0, shards)) {
        uint64_t token = (uint128_t(s) << 64) / shards;
        token >>= sharding_ignore_msb_bits;   // leftmost bits are ignored by zero_based_shard_of
        // token is the start of the next shard, and can be slightly before due to rounding errors; adjust
        while (zero_based_shard_of(token, shards, sharding_ignore_msb_bits) != s) {
            ++token;
        }
        ret[s] = token;
    }
    return ret;
}

unsigned
shard_of(unsigned shard_count, unsigned sharding_ignore_msb_bits, const token& t) {
    switch (t._kind) {
        case token::kind::before_all_keys:
            return token::shard_of_minimum_token();
        case token::kind::after_all_keys:
            return shard_count - 1;
        case token::kind::key:
            uint64_t adjusted = unbias(t);
            return zero_based_shard_of(adjusted, shard_count, sharding_ignore_msb_bits);
    }
    abort();
}

token
token_for_next_shard(const std::vector<uint64_t>& shard_start, unsigned shard_count, unsigned sharding_ignore_msb_bits, const token& t, shard_id shard, unsigned spans) {
    uint64_t n = 0;
    switch (t._kind) {
        case token::kind::before_all_keys:
            break;
        case token::kind::after_all_keys:
            return maximum_token();
        case token::kind::key:
            n = unbias(t);
            break;
    }
    auto s = zero_based_shard_of(n, shard_count, sharding_ignore_msb_bits);

    if (!sharding_ignore_msb_bits) {
        // This ought to be the same as the else branch, but avoids shifts by 64
        n = shard_start[shard];
        if (spans > 1 || shard <= s) {
            return maximum_token();
        }
    } else {
        auto left_part = n >> (64 - sharding_ignore_msb_bits);
        left_part += spans - unsigned(shard > s);
        if (left_part >= (1u << sharding_ignore_msb_bits)) {
            return maximum_token();
        }
        left_part <<= (64 - sharding_ignore_msb_bits);
        auto right_part = shard_start[shard];
        n = left_part | right_part;
    }
    return bias(n);
}

int64_t token::to_int64(token t) {
    return long_token(t);
}

dht::token token::from_int64(int64_t i) {
    return {kind::key, i};
}

static
dht::token find_first_token_for_shard_in_not_wrap_around_range(const dht::sharder& sharder, dht::token start, dht::token end, size_t shard_idx) {
    // Invariant start < end
    // It is guaranteed that start is not MAX_INT64 because end is greater
    auto t = dht::token::from_int64(dht::token::to_int64(start) + 1);
    if (sharder.shard_of(t) != shard_idx) {
        t = sharder.token_for_next_shard(t, shard_idx);
    }
    return std::min(t, end);
}

dht::token find_first_token_for_shard(
        const dht::sharder& sharder, dht::token start, dht::token end, size_t shard_idx) {
    if (start < end) { // Not a wrap around token range
        return find_first_token_for_shard_in_not_wrap_around_range(sharder, start, end, shard_idx);
    } else { // A wrap around token range
        dht::token t;
        if (dht::token::to_int64(start) != std::numeric_limits<int64_t>::max()) {
            t = find_first_token_for_shard_in_not_wrap_around_range(sharder, start, dht::maximum_token(), shard_idx);
            if (!t.is_maximum()) {
                // This means we have found a token for shard shard_idx before 2^63
                return t;
            }
        }
        // No token owned by shard shard_idx was found in (start, 2^63 - 1]
        // so we have to search in (-2^63, end]
        return find_first_token_for_shard_in_not_wrap_around_range(
                sharder, dht::minimum_token(), end, shard_idx);
    }
}

} // namespace dht
<|endoftext|>


<commit_before>#include <mpi.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include "2d-mhd.h"

const int T_MAX=5000;

const int NX=600, NY=600;
const double PI = 3.141592653589793;

Formura_Navigator navi;

float frand() {
  return rand() / float(RAND_MAX);
}

void init() {
  for(int ix = navi.lower_x; ix < navi.upper_x; ++ix) {
    for(int iy = navi.lower_y; iy < navi.upper_y; ++iy) {
      double x = ix/(double)NX, y = iy/(double)NY;
      dens[ix][iy] = 1; // 0;
      vx[ix][iy]   = 0; // -sin(2 * PI * y);
      vy[ix][iy]   = 0; //  sin(2 * PI * x);
      vz[ix][iy]   = 0; // 0;
      Bx[ix][iy]   = 0; // -sin(2 * PI * y);
      By[ix][iy]   = 0; //  sin(4 * PI * x);
      Bz[ix][iy]   = 0; // 0;
      s[ix][iy]    = 0; // 0;
    }
  }
}

int main (int argc, char **argv)
{
  srand(time(NULL));
  MPI_Init(&argc, &argv);
  Formura_Init(&navi, MPI_COMM_WORLD);

  init();

  while(navi.time_step < T_MAX) {
    if(navi.time_step % 10 == 0) {
      printf("t = %d\n", navi.time_step);
      char fn[256];
      sprintf(fn, "out-2d-mhd/dens-%06d.txt", navi.time_step);
      FILE *fp = fopen(fn,"w");
      for(int x = navi.lower_x; x < navi.upper_x; ++x) {
        for(int y = navi.lower_y; y < navi.upper_y; ++y) {
          fprintf(fp, "%d %d %f\n", x, y, dens[x][y]);
        }
        fprintf(fp, "\n");
      }
      fclose(fp);

      sprintf(fn, "out-2d-mhd/vx-%06d.txt", navi.time_step);
      fp = fopen(fn,"w");
      for(int x = navi.lower_x; x < navi.upper_x; ++x) {
        for(int y = navi.lower_y; y < navi.upper_y; ++y) {
          fprintf(fp, "%d %d %f\n", x, y, vx[x][y]);
        }
        fprintf(fp, "\n");
      }
      fclose(fp);

    }
    Formura_Forward(&navi);
  }
  MPI_Finalize();
}
<commit_msg>Test the margin<commit_after>#include <mpi.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include "2d-mhd.h"

const int T_MAX=5000;

const int NX=600, NY=600;
const double PI = 3.141592653589793;

Formura_Navigator navi;

float frand() {
  return rand() / float(RAND_MAX);
}

void init() {
  for(int ix = navi.offset_x + 8+navi.lower_x; ix < navi.upper_x; ++ix) {
    for(int iy = navi.offset_y + 8+navi.lower_y; iy < navi.upper_y; ++iy) {
      double x = ix/(double)NX, y = iy/(double)NY;
      dens[ix][iy] = 1; // 0;
      vx[ix][iy]   = 0; // -sin(2 * PI * y);
      vy[ix][iy]   = 0; //  sin(2 * PI * x);
      vz[ix][iy]   = 0; // 0;
      Bx[ix][iy]   = 0; // -sin(2 * PI * y);
      By[ix][iy]   = 0; //  sin(4 * PI * x);
      Bz[ix][iy]   = 0; // 0;
      s[ix][iy]    = 0; // 0;
    }
  }
}

int main (int argc, char **argv)
{
  srand(time(NULL));
  MPI_Init(&argc, &argv);
  Formura_Init(&navi, MPI_COMM_WORLD);

  init();

  while(navi.time_step < T_MAX) {
    if(navi.time_step % 10 == 0) {
      printf("t = %d\n", navi.time_step);
      char fn[256];
      sprintf(fn, "out-2d-mhd/dens-%06d.txt", navi.time_step);
      FILE *fp = fopen(fn,"w");
      for(int x = navi.lower_x; x < navi.upper_x; ++x) {
        for(int y = navi.lower_y; y < navi.upper_y; ++y) {
          fprintf(fp, "%d %d %f\n", x, y, dens[x][y]);
        }
        fprintf(fp, "\n");
      }
      fclose(fp);

      sprintf(fn, "out-2d-mhd/vx-%06d.txt", navi.time_step);
      fp = fopen(fn,"w");
      for(int x = navi.lower_x; x < navi.upper_x; ++x) {
        for(int y = navi.lower_y; y < navi.upper_y; ++y) {
          fprintf(fp, "%d %d %f\n", x, y, vx[x][y]);
        }
        fprintf(fp, "\n");
      }
      fclose(fp);

    }
    Formura_Forward(&navi);
  }
  MPI_Finalize();
}
<|endoftext|>


<commit_before>#include <cstdio> // sscanf
#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
#include <vector>

extern "C" {
	#include "../../src/ASSInspector.h"
}

struct DialogLine {
	int time;
	std::string line;
};

class ScriptWrapper {
		std::string headerBuffer;
		std::vector<DialogLine> lineBuffers;

	public:
		ScriptWrapper( void );
		void appendHeader( std::string line );
		std::string getHeader( void );
		std::vector<DialogLine> getLines( void );
		void appendDialogue( std::string line, int time );
};

ScriptWrapper::ScriptWrapper( void ) :
headerBuffer( "[Script Info]\n" ) {

}

void ScriptWrapper::appendHeader( std::string line ) {
	headerBuffer += line + "\n";
}

std::string ScriptWrapper::getHeader( void ) {
	return headerBuffer;
}

void ScriptWrapper::appendDialogue( std::string line, int time ) {
	lineBuffers.push_back( {time, line + '\n'} );
}

std::vector<DialogLine> ScriptWrapper::getLines( void ) {
	return lineBuffers;
}

// this should definitely all be one function it is so good
int main( int argc, char **argv ) {
	int width = 0, height = 0;

	if ( 2 > argc ) {
		std::cout << "Usage: " << argv[0] << " infile.ass" << std::endl;
		return 1;
	}

	std::fstream inAss( argv[1], std::ifstream::in );
	if ( inAss.fail( ) ) {
		std::cout << "Failed to open " << argv[1] << std::endl;
	}

	std::string line;
	std::getline( inAss, line );
	if ( "[Script Info]" != line ) {
		// BOM?
		if ( "[Script Info]" != line.substr( 3 ) ) {
			std::cout << "The input file, " << argv[1] << ", does not appear to be valid ASS." << std::endl;
			inAss.close( );
			return 1;
		}
	}

	ScriptWrapper wrapper;

	// This is pretty terrible.
	while ( true ) {
		std::getline( inAss, line );
		if ( '[' == line[0] ) {
			break;
		}
		switch ( line[0] ) {
			case 'S':
				if ( 0 == line.compare( 0, 22, "ScaledBorderAndShadow:" ) ) {
					wrapper.appendHeader( line );
				} else if ( 0 == line.compare( 0, 11, "ScriptType:" ) ) {
					wrapper.appendHeader( line );
				}
				break;
			case 'P':
				if ( 0 == line.compare( 0, 9, "PlayResX:" ) ) {
					std::stringstream converter( line.substr( 9 ) );
					converter >> width;
					wrapper.appendHeader( line );
				} else if ( 0 == line.compare( 0, 9, "PlayResY:" ) ) {
					std::stringstream converter( line.substr( 9 ) );
					converter >> height;
					wrapper.appendHeader( line );
				}
				break;
			case 'W':
				if ( 0 == line.compare( 0, 10, "WrapStyle:" ) ) {
					wrapper.appendHeader( line );
				}
				break;
		}
		if ( !inAss.good( ) ) {
			break;
		}
	}

	wrapper.appendHeader( "[V4+ Styles]" );

	while ( true ) {
		std::getline( inAss, line );
		switch ( line[0] ) {
			case 'S':
				if ( 0 == line.compare( 0, 6, "Style:" ) ) {
					wrapper.appendHeader( line );
				}
				break;
			case 'D':
				if ( 0 == line.compare( 0, 9, "Dialogue:" ) ) {
					int hours = 0, minutes = 0, seconds = 0, centiseconds = 0;
					sscanf( line.substr( 9 ).c_str( ), "%*d,%u:%u:%u.%u", &hours, &minutes, &seconds, &centiseconds );
					int milliseconds = 3600000*hours + 60000*minutes + 1000*seconds + 10*centiseconds;
					wrapper.appendDialogue( line, milliseconds );
				}
				break;
		}
		if ( !inAss.good( ) ) {
			break;
		}
	}

	inAss.close( );

	wrapper.appendHeader( "[Events]" );

	ASSI_State *state = assi_init( width, height, NULL, NULL );

	std::string header = wrapper.getHeader( );
	assi_setHeader( state, header.c_str( ), header.length( ) );

	for ( const auto &pair : wrapper.getLines( ) ) {
		assi_setScript( state, pair.line.c_str( ), pair.line.length( ) );
		ASSI_Rect rect{ 0, 0, 0, 0 };
		assi_calculateBounds( state, &rect, &pair.time, 1 );
		if ( rect.w > 0 && rect.h > 0 ) {
			std::cout << pair.time << " -> " << pair.line;
			std::cout << "x: " << rect.x << " y: " << rect.y << " w: " << rect.w << " h: " << rect.h << std::endl;
		}
	}

	assi_cleanup( state );
	return 0;
}
<commit_msg>C++ example: update to do things with hashes.<commit_after>#include <cstdio> // sscanf
#include <cstdint>
#include <iostream>
#include <fstream>
#include <map>
#include <string>
#include <sstream>
#include <vector>

extern "C" {
	#include "../../src/ASSInspector.h"
}

struct DialogLine {
	int time;
	std::string line;
};

class ScriptWrapper {
		std::string headerBuffer;
		std::vector<DialogLine> lineBuffers;

	public:
		ScriptWrapper( void );
		void appendHeader( std::string line );
		std::string getHeader( void );
		std::vector<DialogLine> getLines( void );
		void appendDialogue( std::string line, int time );
};

ScriptWrapper::ScriptWrapper( void ) :
headerBuffer( "[Script Info]\n" ) {

}

void ScriptWrapper::appendHeader( std::string line ) {
	headerBuffer += line + "\n";
}

std::string ScriptWrapper::getHeader( void ) {
	return headerBuffer;
}

void ScriptWrapper::appendDialogue( std::string line, int time ) {
	lineBuffers.push_back( {time, line + '\n'} );
}

std::vector<DialogLine> ScriptWrapper::getLines( void ) {
	return lineBuffers;
}

// this should definitely all be one function it is so good
int main( int argc, char **argv ) {
	int width = 0, height = 0;

	if ( 2 > argc ) {
		std::cout << "Usage: " << argv[0] << " infile.ass" << std::endl;
		return 1;
	}

	std::fstream inAss( argv[1], std::ifstream::in );
	if ( inAss.fail( ) ) {
		std::cout << "Failed to open " << argv[1] << std::endl;
	}

	std::string line;
	std::getline( inAss, line );
	if ( "[Script Info]" != line ) {
		// BOM?
		if ( "[Script Info]" != line.substr( 3 ) ) {
			std::cout << "The input file, " << argv[1] << ", does not appear to be valid ASS." << std::endl;
			inAss.close( );
			return 1;
		}
	}

	ScriptWrapper wrapper;

	// This is pretty terrible.
	while ( true ) {
		std::getline( inAss, line );
		if ( '[' == line[0] ) {
			break;
		}
		switch ( line[0] ) {
			case 'S':
				if ( 0 == line.compare( 0, 22, "ScaledBorderAndShadow:" ) ) {
					wrapper.appendHeader( line );
				} else if ( 0 == line.compare( 0, 11, "ScriptType:" ) ) {
					wrapper.appendHeader( line );
				}
				break;
			case 'P':
				if ( 0 == line.compare( 0, 9, "PlayResX:" ) ) {
					std::stringstream converter( line.substr( 9 ) );
					converter >> width;
					wrapper.appendHeader( line );
				} else if ( 0 == line.compare( 0, 9, "PlayResY:" ) ) {
					std::stringstream converter( line.substr( 9 ) );
					converter >> height;
					wrapper.appendHeader( line );
				}
				break;
			case 'W':
				if ( 0 == line.compare( 0, 10, "WrapStyle:" ) ) {
					wrapper.appendHeader( line );
				}
				break;
		}
		if ( !inAss.good( ) ) {
			break;
		}
	}

	wrapper.appendHeader( "[V4+ Styles]" );

	while ( true ) {
		std::getline( inAss, line );
		switch ( line[0] ) {
			case 'S':
				if ( 0 == line.compare( 0, 6, "Style:" ) ) {
					wrapper.appendHeader( line );
				}
				break;
			case 'D':
				if ( 0 == line.compare( 0, 9, "Dialogue:" ) ) {
					int hours = 0, minutes = 0, seconds = 0, centiseconds = 0;
					sscanf( line.substr( 9 ).c_str( ), "%*d,%u:%u:%u.%u", &hours, &minutes, &seconds, &centiseconds );
					int milliseconds = 3600000*hours + 60000*minutes + 1000*seconds + 10*centiseconds;
					wrapper.appendDialogue( line, milliseconds );
				}
				break;
		}
		if ( !inAss.good( ) ) {
			break;
		}
	}

	inAss.close( );

	wrapper.appendHeader( "[Events]" );

	ASSI_State *state = assi_init( width, height, NULL, NULL );

	std::string header = wrapper.getHeader( );
	assi_setHeader( state, header.c_str( ), header.length( ) );

	std::map<uint32_t,int> hashes;
	std::map<uint32_t,int>::iterator match;

	int deleted = 0, kept = 0;
	int processed = 0;

	for ( const auto &pair : wrapper.getLines( ) ) {
		assi_setScript( state, pair.line.c_str( ), pair.line.length( ) );
		ASSI_Rect rect{ 0, 0, 0, 0, 0 };
		assi_calculateBounds( state, &rect, &pair.time, 1 );
		++processed;
		match = hashes.find( rect.hash );
		if ( hashes.end( ) != match ) {
			std::cout << "Line " << processed << ": duplicate hash found (line " << match->second << ")" << std::endl;
		} else {
			hashes[rect.hash] = processed;
		}
		if ( rect.w == 0 || rect.h == 0 ) {
			++deleted;
		} else {
			++kept;
		}
	}

	std::cout << "Deleted " << deleted << " lines out of " << processed << std::endl;

	assi_cleanup( state );
	return 0;
}
<|endoftext|>


<commit_before>#include <yamail/resource_pool/async/pool.hpp>

#include <fstream>
#include <iostream>
#include <thread>
#include <memory>

using ofstream_pool = yamail::resource_pool::async::pool<std::unique_ptr<std::ofstream>>;
using time_traits = yamail::resource_pool::time_traits;

struct on_get {
    boost::asio::strand& strand;

    void operator ()(const boost::system::error_code& ec, ofstream_pool::handle handle) {
        try {
            if (ec) {
                std::cerr << ec.message() << std::endl;
                return;
            }
            std::cout << "got resource handle" << std::endl;
            if (handle.empty()) {
                std::unique_ptr<std::ofstream> file(new std::ofstream("pool.log", std::ios::app));
                if (!file->good()) {
                    std::cout << "open file pool.log error: " << file->rdstate() << std::endl;
                    return;
                }
                handle.reset(std::move(file));
            }
            *(handle.get()) << (time_traits::time_point::min() - time_traits::now()).count() << std::endl;
            if (handle.get()->good()) {
                handle.recycle();
            }
        } catch (const std::exception& exception) {
            std::cerr << exception.what() << std::endl;
            return;
        }
    }

    template <typename Func>
    friend void asio_handler_invoke(Func f, on_get* handler) {
        using boost::asio::asio_handler_invoke;
        boost::asio::detail::wrapped_handler<boost::asio::strand, Func, boost::asio::detail::is_continuation_if_running> w(handler->strand, f);
        asio_handler_invoke(f, &w);
    }
};

int main() {
    boost::asio::io_service service;
    boost::asio::strand strand(service);
    ofstream_pool pool(2, 10);
    pool.get_auto_waste(service, on_get {strand}, time_traits::duration::max());
    pool.get_auto_waste(service, on_get {strand}, time_traits::duration::max());
    pool.get_auto_waste(service, on_get {strand}, time_traits::duration::max());
    std::thread worker1([&] { return service.run(); });
    std::thread worker2([&] { return service.run(); });
    worker1.join();
    worker2.join();
    return 0;
}
<commit_msg>Use io_service::strand<commit_after>#include <yamail/resource_pool/async/pool.hpp>

#include <fstream>
#include <iostream>
#include <thread>
#include <memory>

using ofstream_pool = yamail::resource_pool::async::pool<std::unique_ptr<std::ofstream>>;
using time_traits = yamail::resource_pool::time_traits;

struct on_get {
    boost::asio::io_service::strand& strand;

    void operator ()(const boost::system::error_code& ec, ofstream_pool::handle handle) {
        try {
            if (ec) {
                std::cerr << ec.message() << std::endl;
                return;
            }
            std::cout << "got resource handle" << std::endl;
            if (handle.empty()) {
                std::unique_ptr<std::ofstream> file(new std::ofstream("pool.log", std::ios::app));
                if (!file->good()) {
                    std::cout << "open file pool.log error: " << file->rdstate() << std::endl;
                    return;
                }
                handle.reset(std::move(file));
            }
            *(handle.get()) << (time_traits::time_point::min() - time_traits::now()).count() << std::endl;
            if (handle.get()->good()) {
                handle.recycle();
            }
        } catch (const std::exception& exception) {
            std::cerr << exception.what() << std::endl;
            return;
        }
    }

    template <typename Func>
    friend void asio_handler_invoke(Func f, on_get* handler) {
        using boost::asio::asio_handler_invoke;
        boost::asio::detail::wrapped_handler<boost::asio::io_service::strand, Func, boost::asio::detail::is_continuation_if_running> w(handler->strand, f);
        asio_handler_invoke(f, &w);
    }
};

int main() {
    boost::asio::io_service service;
    boost::asio::io_service::strand strand(service);
    ofstream_pool pool(2, 10);
    pool.get_auto_waste(service, on_get {strand}, time_traits::duration::max());
    pool.get_auto_waste(service, on_get {strand}, time_traits::duration::max());
    pool.get_auto_waste(service, on_get {strand}, time_traits::duration::max());
    std::thread worker1([&] { return service.run(); });
    std::thread worker2([&] { return service.run(); });
    worker1.join();
    worker2.join();
    return 0;
}
<|endoftext|>


<commit_before>/*
 * Copyright 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <sys/types.h>

#include <cstring>

#include <media/NdkMediaExtractor.h>
#include <utils/logging.h>
#include <cinttypes>

#include "NDKExtractor.h"

int32_t NDKExtractor::decode(AAsset *asset, uint8_t *targetData, AudioProperties targetProperties) {

    LOGD("Using NDK decoder");

    // open asset as file descriptor
    off_t start, length;
    int fd = AAsset_openFileDescriptor(asset, &start, &length);

    // Extract the audio frames
    AMediaExtractor *extractor = AMediaExtractor_new();
    media_status_t amresult = AMediaExtractor_setDataSourceFd(extractor, fd,
                                                              static_cast<off64_t>(start),
                                                              static_cast<off64_t>(length));
    if (amresult != AMEDIA_OK){
        LOGE("Error setting extractor data source, err %d", amresult);
        return 0;
    }

    // Specify our desired output format by creating it from our source
    AMediaFormat *format = AMediaExtractor_getTrackFormat(extractor, 0);

    int32_t sampleRate;
    if (AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_SAMPLE_RATE, &sampleRate)){
        LOGD("Source sample rate %d", sampleRate);
        if (sampleRate != targetProperties.sampleRate){
            LOGE("Input (%d) and output (%d) sample rates do not match. "
                 "NDK decoder does not support resampling.",
                 sampleRate,
                 targetProperties.sampleRate);
            return 0;
        }
    } else {
        LOGE("Failed to get sample rate");
        return 0;
    };

    int32_t channelCount;
    if (AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_CHANNEL_COUNT, &channelCount)){
        LOGD("Got channel count %d", channelCount);
        if (channelCount != targetProperties.channelCount){
            LOGE("NDK decoder does not support different "
                 "input (%d) and output (%d) channel counts",
                 channelCount,
                 targetProperties.channelCount);
        }
    } else {
        LOGE("Failed to get channel count");
        return 0;
    }

    const char *formatStr = AMediaFormat_toString(format);
    LOGD("Output format %s", formatStr);

    const char *mimeType;
    if (AMediaFormat_getString(format, AMEDIAFORMAT_KEY_MIME, &mimeType)) {
        LOGD("Got mime type %s", mimeType);
    } else {
        LOGE("Failed to get mime type");
        return 0;
    }

    // Obtain the correct decoder
    AMediaCodec *codec = nullptr;
    AMediaExtractor_selectTrack(extractor, 0);
    codec = AMediaCodec_createDecoderByType(mimeType);
    AMediaCodec_configure(codec, format, nullptr, nullptr, 0);
    AMediaCodec_start(codec);

    // DECODE

    bool isExtracting = true;
    bool isDecoding = true;
    int32_t bytesWritten = 0;

    while(isExtracting || isDecoding){

        if (isExtracting){

            // Obtain the index of the next available input buffer
            ssize_t inputIndex = AMediaCodec_dequeueInputBuffer(codec, 2000);
            //LOGV("Got input buffer %d", inputIndex);

            // The input index acts as a status if its negative
            if (inputIndex < 0){
                if (inputIndex == AMEDIACODEC_INFO_TRY_AGAIN_LATER){
                    // LOGV("Codec.dequeueInputBuffer try again later");
                } else {
                    LOGE("Codec.dequeueInputBuffer unknown error status");
                }
            } else {

                // Obtain the actual buffer and read the encoded data into it
                size_t inputSize;
                uint8_t *inputBuffer = AMediaCodec_getInputBuffer(codec, inputIndex, &inputSize);
                //LOGV("Sample size is: %d", inputSize);

                ssize_t sampleSize = AMediaExtractor_readSampleData(extractor, inputBuffer, inputSize);
                auto presentationTimeUs = AMediaExtractor_getSampleTime(extractor);

                if (sampleSize > 0){

                    // Enqueue the encoded data
                    AMediaCodec_queueInputBuffer(codec, inputIndex, 0, sampleSize,
                                                 presentationTimeUs,
                                                 0);
                    AMediaExtractor_advance(extractor);

                } else {
                    LOGD("End of extractor data stream");
                    isExtracting = false;

                    // We need to tell the codec that we've reached the end of the stream
                    AMediaCodec_queueInputBuffer(codec, inputIndex, 0, 0,
                                                 presentationTimeUs,
                                                 AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM);
                }
            }
        }

        if (isDecoding){
            // Dequeue the decoded data
            AMediaCodecBufferInfo info;
            ssize_t outputIndex = AMediaCodec_dequeueOutputBuffer(codec, &info, 0);

            if (outputIndex >= 0){

                // Check whether this is set earlier
                if (info.flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM){
                    LOGD("Reached end of decoding stream");
                    isDecoding = false;
                } // else { // The following code needs to be executed also if end of stream has been reached. Otherwise last samples are not decoded.
                    // Valid index, acquire buffer
                    size_t outputSize;
                    uint8_t *outputBuffer = AMediaCodec_getOutputBuffer(codec, outputIndex, &outputSize);

                    /*LOGV("Got output buffer index %d, buffer size: %d, info size: %d writing to pcm index %d",
                         outputIndex,
                         outputSize,
                         info.size,
                         m_writeIndex);*/

                    // copy the data out of the buffer
                    memcpy(targetData + bytesWritten, outputBuffer, info.size);
                    bytesWritten+=info.size;
                    AMediaCodec_releaseOutputBuffer(codec, outputIndex, false);
               // }

            } else {

                // The outputIndex doubles as a status return if its value is < 0
                switch(outputIndex){
                    case AMEDIACODEC_INFO_TRY_AGAIN_LATER:
                        LOGD("dequeueOutputBuffer: try again later");
                        break;
                    case AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED:
                        LOGD("dequeueOutputBuffer: output buffers changed");
                        break;
                    case AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED:
                        LOGD("dequeueOutputBuffer: output outputFormat changed");
                        format = AMediaCodec_getOutputFormat(codec);
                        LOGD("outputFormat changed to: %s", AMediaFormat_toString(format));
                        break;
                }
            }
        }
    }

    // Clean up
    AMediaFormat_delete(format);
    AMediaCodec_delete(codec);
    AMediaExtractor_delete(extractor);

    return bytesWritten;
}
<commit_msg>Update NDKExtractor.cpp<commit_after>/*
 * Copyright 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <sys/types.h>

#include <cstring>

#include <media/NdkMediaExtractor.h>
#include <utils/logging.h>
#include <cinttypes>

#include "NDKExtractor.h"

int32_t NDKExtractor::decode(AAsset *asset, uint8_t *targetData, AudioProperties targetProperties) {

    LOGD("Using NDK decoder");

    // open asset as file descriptor
    off_t start, length;
    int fd = AAsset_openFileDescriptor(asset, &start, &length);

    // Extract the audio frames
    AMediaExtractor *extractor = AMediaExtractor_new();
    media_status_t amresult = AMediaExtractor_setDataSourceFd(extractor, fd,
                                                              static_cast<off64_t>(start),
                                                              static_cast<off64_t>(length));
    if (amresult != AMEDIA_OK){
        LOGE("Error setting extractor data source, err %d", amresult);
        return 0;
    }

    // Specify our desired output format by creating it from our source
    AMediaFormat *format = AMediaExtractor_getTrackFormat(extractor, 0);

    int32_t sampleRate;
    if (AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_SAMPLE_RATE, &sampleRate)){
        LOGD("Source sample rate %d", sampleRate);
        if (sampleRate != targetProperties.sampleRate){
            LOGE("Input (%d) and output (%d) sample rates do not match. "
                 "NDK decoder does not support resampling.",
                 sampleRate,
                 targetProperties.sampleRate);
            return 0;
        }
    } else {
        LOGE("Failed to get sample rate");
        return 0;
    };

    int32_t channelCount;
    if (AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_CHANNEL_COUNT, &channelCount)){
        LOGD("Got channel count %d", channelCount);
        if (channelCount != targetProperties.channelCount){
            LOGE("NDK decoder does not support different "
                 "input (%d) and output (%d) channel counts",
                 channelCount,
                 targetProperties.channelCount);
        }
    } else {
        LOGE("Failed to get channel count");
        return 0;
    }

    const char *formatStr = AMediaFormat_toString(format);
    LOGD("Output format %s", formatStr);

    const char *mimeType;
    if (AMediaFormat_getString(format, AMEDIAFORMAT_KEY_MIME, &mimeType)) {
        LOGD("Got mime type %s", mimeType);
    } else {
        LOGE("Failed to get mime type");
        return 0;
    }

    // Obtain the correct decoder
    AMediaCodec *codec = nullptr;
    AMediaExtractor_selectTrack(extractor, 0);
    codec = AMediaCodec_createDecoderByType(mimeType);
    AMediaCodec_configure(codec, format, nullptr, nullptr, 0);
    AMediaCodec_start(codec);

    // DECODE

    bool isExtracting = true;
    bool isDecoding = true;
    int32_t bytesWritten = 0;

    while(isExtracting || isDecoding){

        if (isExtracting){

            // Obtain the index of the next available input buffer
            ssize_t inputIndex = AMediaCodec_dequeueInputBuffer(codec, 2000);
            //LOGV("Got input buffer %d", inputIndex);

            // The input index acts as a status if its negative
            if (inputIndex < 0){
                if (inputIndex == AMEDIACODEC_INFO_TRY_AGAIN_LATER){
                    // LOGV("Codec.dequeueInputBuffer try again later");
                } else {
                    LOGE("Codec.dequeueInputBuffer unknown error status");
                }
            } else {

                // Obtain the actual buffer and read the encoded data into it
                size_t inputSize;
                uint8_t *inputBuffer = AMediaCodec_getInputBuffer(codec, inputIndex, &inputSize);
                //LOGV("Sample size is: %d", inputSize);

                ssize_t sampleSize = AMediaExtractor_readSampleData(extractor, inputBuffer, inputSize);
                auto presentationTimeUs = AMediaExtractor_getSampleTime(extractor);

                if (sampleSize > 0){

                    // Enqueue the encoded data
                    AMediaCodec_queueInputBuffer(codec, inputIndex, 0, sampleSize,
                                                 presentationTimeUs,
                                                 0);
                    AMediaExtractor_advance(extractor);

                } else {
                    LOGD("End of extractor data stream");
                    isExtracting = false;

                    // We need to tell the codec that we've reached the end of the stream
                    AMediaCodec_queueInputBuffer(codec, inputIndex, 0, 0,
                                                 presentationTimeUs,
                                                 AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM);
                }
            }
        }

        if (isDecoding){
            // Dequeue the decoded data
            AMediaCodecBufferInfo info;
            ssize_t outputIndex = AMediaCodec_dequeueOutputBuffer(codec, &info, 0);

            if (outputIndex >= 0){

                // Check whether this is set earlier
                if (info.flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM){
                    LOGD("Reached end of decoding stream");
                    isDecoding = false;
                }

                // Valid index, acquire buffer
                size_t outputSize;
                uint8_t *outputBuffer = AMediaCodec_getOutputBuffer(codec, outputIndex, &outputSize);

                /*LOGV("Got output buffer index %d, buffer size: %d, info size: %d writing to pcm index %d",
                     outputIndex,
                     outputSize,
                     info.size,
                     m_writeIndex);*/

                // copy the data out of the buffer
                memcpy(targetData + bytesWritten, outputBuffer, info.size);
                bytesWritten+=info.size;
                AMediaCodec_releaseOutputBuffer(codec, outputIndex, false);
            } else {

                // The outputIndex doubles as a status return if its value is < 0
                switch(outputIndex){
                    case AMEDIACODEC_INFO_TRY_AGAIN_LATER:
                        LOGD("dequeueOutputBuffer: try again later");
                        break;
                    case AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED:
                        LOGD("dequeueOutputBuffer: output buffers changed");
                        break;
                    case AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED:
                        LOGD("dequeueOutputBuffer: output outputFormat changed");
                        format = AMediaCodec_getOutputFormat(codec);
                        LOGD("outputFormat changed to: %s", AMediaFormat_toString(format));
                        break;
                }
            }
        }
    }

    // Clean up
    AMediaFormat_delete(format);
    AMediaCodec_delete(codec);
    AMediaExtractor_delete(extractor);

    return bytesWritten;
}
<|endoftext|>