<commit_before>/*************** <auto-copyright.pl BEGIN do not edit this line> **************
 *
 * VR Juggler is (C) Copyright 1998-2003 by Iowa State University
 *
 * Original Authors:
 *   Allen Bierbaum, Christopher Just,
 *   Patrick Hartling, Kevin Meinert,
 *   Carolina Cruz-Neira, Albert Baker
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 *
 * -----------------------------------------------------------------
 * File:          $RCSfile$
 * Date modified: $Date$
 * Version:       $Revision$
 * -----------------------------------------------------------------
 *
 *************** <auto-copyright.pl END do not edit this line> ***************/

#include <gadget/Devices/DriverConfig.h>

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

#include <vpr/Util/Debug.h>
#include <jccl/Config/ConfigElement.h>
#include <gadget/Type/DeviceConstructor.h>
#include <gadget/Util/Debug.h>
#include <drivers/Microsoft/DirectXJoystick/DirectXJoystick.h>

extern "C"
{

GADGET_DRIVER_EXPORT(void) initDevice(gadget::InputManager* inputMgr)
{
   new gadget::DeviceConstructor<gadget::DirectXJoystick>(inputMgr);
}

}

namespace gadget
{

/** Constructor. */
DirectXJoystick::DirectXJoystick()
   : mActive(false)
{
}

/**
 * Destructor.
 *
 * @pre None.
 * @post Shared memory is released.
 */
DirectXJoystick::~DirectXJoystick()
{
}

std::string DirectXJoystick::getElementType()
{
   return "directx_joystick";
}

/**
 * config
 */
bool DirectXJoystick::config(jccl::ConfigElementPtr e)
{
   if(! (Input::config(e) && Digital::config(e) && Analog::config(e)))
   {
      return false;
   }

   mJsLabel = e->getName();

   unsigned num_axis_buttons = e->getNum("axis_buttons");
   for ( unsigned i = 0; i < num_axis_buttons; ++i )
   {
      unsigned idx = e->getProperty<int>("axis_buttons", i);
      mAxisButtonIndices.push_back(idx);
   }

   // XXX: This doesn't make any sense.  PH 8/19/2004
   if( getMin() == 0 && getMax() == 0 || getMin() >= getMax() )
   {
      setMin(-100.0f);
      setMax(100.0f);
   }

   return true;
}

bool DirectXJoystick::startSampling()
{
   vprDEBUG(gadgetDBG_INPUT_MGR, vprDBG_CONFIG_STATUS_LVL)
      << "Opening Win Joystick driver " << std::endl << vprDEBUG_FLUSH;

   mActive = (mInputDrv.init() == 0);

   if ( ! mActive )
   {
      vprDEBUG(gadgetDBG_INPUT_MGR, vprDBG_CRITICAL_LVL)
         << "ERROR: Failed to open Win Joystick." << std::endl
         << vprDEBUG_FLUSH;
      return false;
   }

   // FIXME: hardcoded number of axes and buttons
   int version = 0;
   mNumAxes = 9;       // Analog = 8   from DIJOYSTATE
   mNumButtons = 10;   // Digital = 32  from DIJOYSTATE

   // Output joystick description
   vprDEBUG(gadgetDBG_INPUT_MGR, vprDBG_CONFIG_LVL)
         << "  joystick label: " << mJsLabel << std::endl
         << "   joystick name: " << mInputDrv.getProductName() << std::endl
         << "            axes: " << mNumAxes << std::endl
         << "         buttons: " << mNumButtons << std::endl
         << "      driver ver: " << version << std::endl
         << "    axis buttons: ";

   for(unsigned i=0;i<mAxisButtonIndices.size(); ++i)
   {
      vprDEBUG_CONTnl(gadgetDBG_INPUT_MGR, vprDBG_CONFIG_LVL)
         << mAxisButtonIndices[i] << " ";
   }
   vprDEBUG_CONTnl(gadgetDBG_INPUT_MGR, vprDBG_CONFIG_LVL)
      << std::endl << vprDEBUG_FLUSH;

   // Allocate initial device data
   // - By default this will clear them out
   mCurAxes.resize(mNumAxes);
   mCurAxesRanges.resize(mNumAxes, axis_range_t(-100.0f, 100.0f));             // Initialize ranges to 0,255
   mCurButtons.resize(mNumButtons + mAxisButtonIndices.size());

   // Setup axis as button stuff
   mAxisToButtonIndexLookup.clear();
   mAxisToButtonIndexLookup.resize(mNumAxes, -1);            // Default to -1, meaning no axis button
   for(unsigned i=0;i<mAxisButtonIndices.size(); ++i)       // For each configured axis index
   {
      unsigned virtual_btn_index = (mNumButtons+i);                     // Index of the virtual button from the axis
      vprASSERT(virtual_btn_index < mCurButtons.size() && "Virtual button index out of range");
      unsigned axis_index = mAxisButtonIndices[i];                      // Index of the axis we are mapping
      mAxisToButtonIndexLookup[axis_index] = int(virtual_btn_index);    // Setup the mapping
   }

   return true;
}

/**
 * Stops sampling.
 * Drop connection to joystick and clear everything.
 */
bool DirectXJoystick::stopSampling()
{
   if ( mActive )
   {
      mInputDrv.close();  // Close the joystick device
      mActive = false;
   }

   return true;
}

/**
 * Updates to the sampled data.
 *
 * @pre None.
 * @post Most recent value is copied over to temp area.
 */
void DirectXJoystick::updateData()
{
   mInputDrv.poll();
   DIJOYSTATE  mJsData = mInputDrv.getData();

   //FIXME: if & only if there is events happen, do setTime
   // for buttons, do update
   for ( unsigned int i = 0; i < mCurButtons.size(); ++i )
   {
      mCurButtons[i].setDigital(mJsData.rgbButtons[i]);
      if( mCurButtons[i].getDigital() != 0)
      {
         mCurButtons[i].setTime();
      }
   }

   // for axes, do update
   float norm_value;
   normalizeMinToMax(mJsData.lX, norm_value);
   mCurAxes[0] = norm_value;
   normalizeMinToMax(mJsData.lY, norm_value);
   mCurAxes[1] = norm_value;
   normalizeMinToMax(mJsData.lZ, norm_value);
   mCurAxes[2] = norm_value;
   normalizeMinToMax(mJsData.lRx, norm_value); // x rotation
   mCurAxes[3] = norm_value;
   normalizeMinToMax(mJsData.lRy, norm_value); // y rotation
   mCurAxes[4] = norm_value;
   normalizeMinToMax(mJsData.lRz, norm_value); // z rotation
   mCurAxes[5] = norm_value;
   normalizeMinToMax(mJsData.rglSlider[0], norm_value); // u-axis
   mCurAxes[6] = norm_value;
   normalizeMinToMax(mJsData.rglSlider[1], norm_value); // v-axis
   mCurAxes[7] = norm_value;
   mCurAxes[8] = (float(mJsData.rgdwPOV[0])); //hat: -1, 0, 9,000, 18,000, or 27,000.

   // use axes as button, only first 3 are tested.
   for ( int axis_number = 0; axis_number < mNumAxes; ++axis_number )
   {
      // FIXME: don't know when it is changed
      mCurAxes[axis_number].setTime();

      // Check for axis buttons
      // - If we have a mapping
      // - If axis is gt 0.5, then btn is down
      if ( axis_number < 3 )
      {
         float norm_value(0.0f); // mCurAxes[axis_number].getAnalog();
         if ( mAxisToButtonIndexLookup[axis_number] != -1 ) // If we map to a virtual button
         {
            unsigned vir_btn_index = mAxisToButtonIndexLookup[axis_number];
            vprASSERT(vir_btn_index < mCurButtons.size() && "Virtual button index out of range");
            mCurButtons[vir_btn_index] = ((norm_value > 0.5f) ? 1 : 0);
            mCurButtons[vir_btn_index].setTime();
         }
      }
   }

   addDigitalSample(mCurButtons);
   swapDigitalBuffers();

   addAnalogSample(mCurAxes);
   swapAnalogBuffers();
} // end of Update

} // End of gadget namespace
<commit_msg>Silenced a compiler warning.<commit_after>/*************** <auto-copyright.pl BEGIN do not edit this line> **************
 *
 * VR Juggler is (C) Copyright 1998-2003 by Iowa State University
 *
 * Original Authors:
 *   Allen Bierbaum, Christopher Just,
 *   Patrick Hartling, Kevin Meinert,
 *   Carolina Cruz-Neira, Albert Baker
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 *
 * -----------------------------------------------------------------
 * File:          $RCSfile$
 * Date modified: $Date$
 * Version:       $Revision$
 * -----------------------------------------------------------------
 *
 *************** <auto-copyright.pl END do not edit this line> ***************/

#include <gadget/Devices/DriverConfig.h>

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

#include <vpr/Util/Debug.h>
#include <jccl/Config/ConfigElement.h>
#include <gadget/Type/DeviceConstructor.h>
#include <gadget/Util/Debug.h>
#include <drivers/Microsoft/DirectXJoystick/DirectXJoystick.h>

extern "C"
{

GADGET_DRIVER_EXPORT(void) initDevice(gadget::InputManager* inputMgr)
{
   new gadget::DeviceConstructor<gadget::DirectXJoystick>(inputMgr);
}

}

namespace gadget
{

/** Constructor. */
DirectXJoystick::DirectXJoystick()
   : mActive(false)
{
}

/**
 * Destructor.
 *
 * @pre None.
 * @post Shared memory is released.
 */
DirectXJoystick::~DirectXJoystick()
{
}

std::string DirectXJoystick::getElementType()
{
   return "directx_joystick";
}

/**
 * config
 */
bool DirectXJoystick::config(jccl::ConfigElementPtr e)
{
   if(! (Input::config(e) && Digital::config(e) && Analog::config(e)))
   {
      return false;
   }

   mJsLabel = e->getName();

   unsigned num_axis_buttons = e->getNum("axis_buttons");
   for ( unsigned i = 0; i < num_axis_buttons; ++i )
   {
      unsigned idx = e->getProperty<int>("axis_buttons", i);
      mAxisButtonIndices.push_back(idx);
   }

   // XXX: This doesn't make any sense.  PH 8/19/2004
   if( getMin() == 0 && getMax() == 0 || getMin() >= getMax() )
   {
      setMin(-100.0f);
      setMax(100.0f);
   }

   return true;
}

bool DirectXJoystick::startSampling()
{
   vprDEBUG(gadgetDBG_INPUT_MGR, vprDBG_CONFIG_STATUS_LVL)
      << "Opening Win Joystick driver " << std::endl << vprDEBUG_FLUSH;

   mActive = (mInputDrv.init() == 0);

   if ( ! mActive )
   {
      vprDEBUG(gadgetDBG_INPUT_MGR, vprDBG_CRITICAL_LVL)
         << "ERROR: Failed to open Win Joystick." << std::endl
         << vprDEBUG_FLUSH;
      return false;
   }

   // FIXME: hardcoded number of axes and buttons
   int version = 0;
   mNumAxes = 9;       // Analog = 8   from DIJOYSTATE
   mNumButtons = 10;   // Digital = 32  from DIJOYSTATE

   // Output joystick description
   vprDEBUG(gadgetDBG_INPUT_MGR, vprDBG_CONFIG_LVL)
         << "  joystick label: " << mJsLabel << std::endl
         << "   joystick name: " << mInputDrv.getProductName() << std::endl
         << "            axes: " << mNumAxes << std::endl
         << "         buttons: " << mNumButtons << std::endl
         << "      driver ver: " << version << std::endl
         << "    axis buttons: ";

   for(unsigned i=0;i<mAxisButtonIndices.size(); ++i)
   {
      vprDEBUG_CONTnl(gadgetDBG_INPUT_MGR, vprDBG_CONFIG_LVL)
         << mAxisButtonIndices[i] << " ";
   }
   vprDEBUG_CONTnl(gadgetDBG_INPUT_MGR, vprDBG_CONFIG_LVL)
      << std::endl << vprDEBUG_FLUSH;

   // Allocate initial device data
   // - By default this will clear them out
   mCurAxes.resize(mNumAxes);
   mCurAxesRanges.resize(mNumAxes, axis_range_t(-100.0f, 100.0f));             // Initialize ranges to 0,255
   mCurButtons.resize(mNumButtons + mAxisButtonIndices.size());

   // Setup axis as button stuff
   mAxisToButtonIndexLookup.clear();
   mAxisToButtonIndexLookup.resize(mNumAxes, -1);            // Default to -1, meaning no axis button
   for(unsigned i=0;i<mAxisButtonIndices.size(); ++i)       // For each configured axis index
   {
      unsigned virtual_btn_index = (mNumButtons+i);                     // Index of the virtual button from the axis
      vprASSERT(virtual_btn_index < mCurButtons.size() && "Virtual button index out of range");
      unsigned axis_index = mAxisButtonIndices[i];                      // Index of the axis we are mapping
      mAxisToButtonIndexLookup[axis_index] = int(virtual_btn_index);    // Setup the mapping
   }

   return true;
}

/**
 * Stops sampling.
 * Drop connection to joystick and clear everything.
 */
bool DirectXJoystick::stopSampling()
{
   if ( mActive )
   {
      mInputDrv.close();  // Close the joystick device
      mActive = false;
   }

   return true;
}

/**
 * Updates to the sampled data.
 *
 * @pre None.
 * @post Most recent value is copied over to temp area.
 */
void DirectXJoystick::updateData()
{
   mInputDrv.poll();
   DIJOYSTATE  mJsData = mInputDrv.getData();

   //FIXME: if & only if there is events happen, do setTime
   // for buttons, do update
   for ( unsigned int i = 0; i < mCurButtons.size(); ++i )
   {
      mCurButtons[i].setDigital(mJsData.rgbButtons[i]);
      if( mCurButtons[i].getDigital() != 0)
      {
         mCurButtons[i].setTime();
      }
   }

   // for axes, do update
   float norm_value;
   normalizeMinToMax(mJsData.lX, norm_value);
   mCurAxes[0] = norm_value;
   normalizeMinToMax(mJsData.lY, norm_value);
   mCurAxes[1] = norm_value;
   normalizeMinToMax(mJsData.lZ, norm_value);
   mCurAxes[2] = norm_value;
   normalizeMinToMax(mJsData.lRx, norm_value); // x rotation
   mCurAxes[3] = norm_value;
   normalizeMinToMax(mJsData.lRy, norm_value); // y rotation
   mCurAxes[4] = norm_value;
   normalizeMinToMax(mJsData.lRz, norm_value); // z rotation
   mCurAxes[5] = norm_value;
   normalizeMinToMax(mJsData.rglSlider[0], norm_value); // u-axis
   mCurAxes[6] = norm_value;
   normalizeMinToMax(mJsData.rglSlider[1], norm_value); // v-axis
   mCurAxes[7] = norm_value;
   mCurAxes[8] = (float(mJsData.rgdwPOV[0])); //hat: -1, 0, 9,000, 18,000, or 27,000.

   // use axes as button, only first 3 are tested.
   for ( unsigned int axis_number = 0; axis_number < mNumAxes; ++axis_number )
   {
      // FIXME: don't know when it is changed
      mCurAxes[axis_number].setTime();

      // Check for axis buttons
      // - If we have a mapping
      // - If axis is gt 0.5, then btn is down
      if ( axis_number < 3 )
      {
         float norm_value(0.0f); // mCurAxes[axis_number].getAnalog();
         if ( mAxisToButtonIndexLookup[axis_number] != -1 ) // If we map to a virtual button
         {
            unsigned vir_btn_index = mAxisToButtonIndexLookup[axis_number];
            vprASSERT(vir_btn_index < mCurButtons.size() && "Virtual button index out of range");
            mCurButtons[vir_btn_index] = ((norm_value > 0.5f) ? 1 : 0);
            mCurButtons[vir_btn_index].setTime();
         }
      }
   }

   addDigitalSample(mCurButtons);
   swapDigitalBuffers();

   addAnalogSample(mCurAxes);
   swapAnalogBuffers();
} // end of Update

} // End of gadget namespace
<|endoftext|>


<commit_before>/** \brief Utility for deleting partial or entire MARC records based on an input list.
 *  \author Mario Trojan (mario.trojan@uni-tuebingen.de)
 *
 *  \copyright 2018 Universitätsbibliothek Tübingen.  All rights reserved.
 *
 *  This program 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.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <iostream>
#include <map>
#include <memory>
#include <cstdlib>
#include "Downloader.h"
#include "File.h"
#include "FileUtil.h"
#include "MARC.h"
#include "StringUtil.h"
#include "util.h"

namespace {

[[noreturn]] void Usage() {
    std::cerr << "Usage: " << ::progname
              << " [--input-format=(marc-21|marc-xml)] [--output-format=(marc-21|marc-xml)] input_marc21 output_marc21\n";
    std::exit(EXIT_FAILURE);
}

// TODO: add archivportal-d (BEACON url not found)
// BEACON file can only be used if entry elemtents are plain GND numbers.
const std::map<std::string, std::string> beacon_id_to_url_map({ { "kalliope", "http://kalliope.staatsbibliothek-berlin.de/beacon/beacon.txt" } });

std::map<std::string, std::set<std::string>> PopulateGNDToBeaconIdsMap() {
    std::map<std::string, std::set<std::string>> gnd_to_beacon_ids_map;

    FileUtil::AutoTempDirectory temp_dir;
    for (const auto &beacon_id_and_url : beacon_id_to_url_map) {
        const std::string beacon_id(beacon_id_and_url.first);
        const std::string beacon_url(beacon_id_and_url.second);
        const std::string beacon_temp_path(temp_dir.getDirectoryPath() + "/" + beacon_id);

        LOG_INFO("Downloading/Processing " + beacon_id + " BEACON file from " + beacon_url);
        if (not Download(beacon_url, beacon_temp_path, Downloader::DEFAULT_TIME_LIMIT))
            LOG_ERROR("BEACON file could not be downloaded: " + beacon_url);

        File beacon_file(beacon_temp_path, "r");
        unsigned beacon_gnd_count(0);
        while (not beacon_file.eof()) {
            const std::string line(beacon_file.getline());
            if (not StringUtil::StartsWith(line, "#")) {
                const std::string gnd(line);
                ++beacon_gnd_count;
                auto gnd_to_beacon_ids_iter(gnd_to_beacon_ids_map.find(gnd));
                if (gnd_to_beacon_ids_iter == gnd_to_beacon_ids_map.end())
                    gnd_to_beacon_ids_map.emplace(gnd, std::initializer_list<std::string>{ beacon_id });
                else
                    gnd_to_beacon_ids_iter->second.emplace(beacon_id);
            }
        }
        LOG_INFO("Found " + std::to_string(beacon_gnd_count) + " GND numbers in " + beacon_id + " BEACON file.");
    }

    return gnd_to_beacon_ids_map;
}

void ProcessRecords(const std::map<std::string, std::set<std::string>> &gnd_to_beacon_ids_map,
                    MARC::Reader * const marc_reader, MARC::Writer * const marc_writer)
{
    unsigned beacon_reference_count(0);
    while (MARC::Record record = marc_reader->read()) {
        std::string gnd;
        if (MARC::GetGNDCode(record, &gnd)) {
            const auto gnd_to_beacon_ids(gnd_to_beacon_ids_map.find(gnd));
            if (gnd_to_beacon_ids != gnd_to_beacon_ids_map.end()) {
                MARC::Record::Field beacon_field("BEA");
                beacon_field.appendSubfield('a', "1");
                for (const std::string &beacon_id : gnd_to_beacon_ids->second) {
                    beacon_field.appendSubfield('b', beacon_id);
                    ++beacon_reference_count;
                }

                record.insertField(beacon_field);
            }
        }

        marc_writer->write(record);
    }

    LOG_INFO("Added " + std::to_string(beacon_reference_count) + " BEACON references to MARC records!");
}

} // unnamed namespace

int Main(int argc, char *argv[]) {
    ::progname = argv[0];

    if (argc < 3)
        Usage();

    const auto reader_type(MARC::GetOptionalReaderType(&argc, &argv, 1));
    const auto writer_type(MARC::GetOptionalWriterType(&argc, &argv, 1));

    if (argc != 3)
        Usage();

    const auto marc_reader(MARC::Reader::Factory(argv[1], reader_type));
    const auto marc_writer(MARC::Writer::Factory(argv[2], writer_type));

    std::map<std::string, std::set<std::string>> gnd_to_beacon_ids_map(PopulateGNDToBeaconIdsMap());
    ProcessRecords(gnd_to_beacon_ids_map, marc_reader.get(), marc_writer.get());

    return EXIT_SUCCESS;
}
<commit_msg>removed add_beacon_references input & output format override flags<commit_after>/** \brief Utility for adding external BEACON references to MARC data.
 *  \author Mario Trojan (mario.trojan@uni-tuebingen.de)
 *
 *  \copyright 2018 Universitätsbibliothek Tübingen.  All rights reserved.
 *
 *  This program 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.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <iostream>
#include <map>
#include <memory>
#include <cstdlib>
#include "Downloader.h"
#include "File.h"
#include "FileUtil.h"
#include "MARC.h"
#include "StringUtil.h"
#include "util.h"

namespace {

[[noreturn]] void Usage() {
    std::cerr << "Usage: " << ::progname
              << " input_marc output_marc\n";
    std::exit(EXIT_FAILURE);
}

// TODO: add archivportal-d (BEACON url not found)
// BEACON file can only be used if entry elemtents are plain GND numbers.
const std::map<std::string, std::string> beacon_id_to_url_map({ { "kalliope", "http://kalliope.staatsbibliothek-berlin.de/beacon/beacon.txt" } });

std::map<std::string, std::set<std::string>> PopulateGNDToBeaconIdsMap() {
    std::map<std::string, std::set<std::string>> gnd_to_beacon_ids_map;

    FileUtil::AutoTempDirectory temp_dir;
    for (const auto &beacon_id_and_url : beacon_id_to_url_map) {
        const std::string beacon_id(beacon_id_and_url.first);
        const std::string beacon_url(beacon_id_and_url.second);
        const std::string beacon_temp_path(temp_dir.getDirectoryPath() + "/" + beacon_id);

        LOG_INFO("Downloading/Processing " + beacon_id + " BEACON file from " + beacon_url);
        if (not Download(beacon_url, beacon_temp_path, Downloader::DEFAULT_TIME_LIMIT))
            LOG_ERROR("BEACON file could not be downloaded: " + beacon_url);

        File beacon_file(beacon_temp_path, "r");
        unsigned beacon_gnd_count(0);
        while (not beacon_file.eof()) {
            const std::string line(beacon_file.getline());
            if (not StringUtil::StartsWith(line, "#")) {
                const std::string gnd(line);
                ++beacon_gnd_count;
                auto gnd_to_beacon_ids_iter(gnd_to_beacon_ids_map.find(gnd));
                if (gnd_to_beacon_ids_iter == gnd_to_beacon_ids_map.end())
                    gnd_to_beacon_ids_map.emplace(gnd, std::initializer_list<std::string>{ beacon_id });
                else
                    gnd_to_beacon_ids_iter->second.emplace(beacon_id);
            }
        }
        LOG_INFO("Found " + std::to_string(beacon_gnd_count) + " GND numbers in " + beacon_id + " BEACON file.");
    }

    return gnd_to_beacon_ids_map;
}

void ProcessRecords(const std::map<std::string, std::set<std::string>> &gnd_to_beacon_ids_map,
                    MARC::Reader * const marc_reader, MARC::Writer * const marc_writer)
{
    unsigned beacon_reference_count(0);
    while (MARC::Record record = marc_reader->read()) {
        std::string gnd;
        if (MARC::GetGNDCode(record, &gnd)) {
            const auto gnd_to_beacon_ids(gnd_to_beacon_ids_map.find(gnd));
            if (gnd_to_beacon_ids != gnd_to_beacon_ids_map.end()) {
                MARC::Record::Field beacon_field("BEA");
                beacon_field.appendSubfield('a', "1");
                for (const std::string &beacon_id : gnd_to_beacon_ids->second) {
                    beacon_field.appendSubfield('b', beacon_id);
                    ++beacon_reference_count;
                }

                record.insertField(beacon_field);
            }
        }

        marc_writer->write(record);
    }

    LOG_INFO("Added " + std::to_string(beacon_reference_count) + " BEACON references to MARC records!");
}

} // unnamed namespace

int Main(int argc, char *argv[]) {
    ::progname = argv[0];

    if (argc != 3)
        Usage();

    const auto marc_reader(MARC::Reader::Factory(argv[1]));
    const auto marc_writer(MARC::Writer::Factory(argv[2]));

    std::map<std::string, std::set<std::string>> gnd_to_beacon_ids_map(PopulateGNDToBeaconIdsMap());
    ProcessRecords(gnd_to_beacon_ids_map, marc_reader.get(), marc_writer.get());

    return EXIT_SUCCESS;
}
<|endoftext|>


<commit_before>//===----------------------------------------------------------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is dual licensed under the MIT and the University of Illinois Open
// Source Licenses. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

// <vector>

// template <class... Args> reference emplace_back(Args&&... args);

//#include <vector>
//#include <cassert>
//#include "../../../stack_allocator.h"
//#include "min_allocator.h"
//#include "asan_testing.h"

#ifndef _LIBCPP_HAS_NO_RVALUE_REFERENCES

class A
{
    int i_;
    double d_;

    A(const A&);
    A& operator=(const A&);
public:
    A(int i, double d)
        : i_(i), d_(d) {}

    A(A&& a)
        : i_(a.i_),
          d_(a.d_)
    {
        a.i_ = 0;
        a.d_ = 0;
    }

    A& operator=(A&& a)
    {
        i_ = a.i_;
        d_ = a.d_;
        a.i_ = 0;
        a.d_ = 0;
        return *this;
    }

    int geti() const {return i_;}
    double getd() const {return d_;}
};

#endif  // _LIBCPP_HAS_NO_RVALUE_REFERENCES

void main()
{
#ifndef _LIBCPP_HAS_NO_VARIADICS
#ifndef _LIBCPP_HAS_NO_RVALUE_REFERENCES
    {
        vector<A> c;
        A& r1 = c.emplace_back(2, 3.5);
        assert(c.size() == 1);
        assert(&r1 == &c.back());
        assert(c.front().geti() == 2);
        assert(c.front().getd() == 3.5);
        //assert(is_contiguous_container_asan_correct(c));
        A& r2 = c.emplace_back(3, 4.5);
        assert(c.size() == 2);
        assert(&r2 == &c.back());
        assert(c.front().geti() == 2);
        assert(c.front().getd() == 3.5);
        assert(c.back().geti() == 3);
        assert(c.back().getd() == 4.5);
        //assert(is_contiguous_container_asan_correct(c));
    }
#ifdef LIBCPP_TEST_STACK_ALLOCATOR
    {
        vector<A, stack_allocator<A, 4> > c;
        A& r1 = c.emplace_back(2, 3.5);
        assert(c.size() == 1);
        assert(&r1 == &c.back());
        assert(c.front().geti() == 2);
        assert(c.front().getd() == 3.5);
        //assert(is_contiguous_container_asan_correct(c));
        A& r2 = c.emplace_back(3, 4.5);
        assert(c.size() == 2);
        assert(&r2 == &c.back());
        assert(c.front().geti() == 2);
        assert(c.front().getd() == 3.5);
        assert(c.back().geti() == 3);
        assert(c.back().getd() == 4.5);
        //assert(is_contiguous_container_asan_correct(c));
    }
#endif
//#if __cplusplus >= 201103L
#ifdef LIBCPP_TEST_MIN_ALLOCATOR
    {
        vector<A, min_allocator<A>> c;
        A& r1 = c.emplace_back(2, 3.5);
        assert(c.size() == 1);
        assert(&r1 == &c.back());
        assert(c.front().geti() == 2);
        assert(c.front().getd() == 3.5);
        //assert(is_contiguous_container_asan_correct(c));
        A& r2 = c.emplace_back(3, 4.5);
        assert(c.size() == 2);
        assert(&r2 == &c.back());
        assert(c.front().geti() == 2);
        assert(c.front().getd() == 3.5);
        assert(c.back().geti() == 3);
        assert(c.back().getd() == 4.5);
        //assert(is_contiguous_container_asan_correct(c));
    }
#endif
#endif  // _LIBCPP_HAS_NO_RVALUE_REFERENCES
#endif	// _LIBCPP_HAS_NO_VARIADICS
}
<commit_msg>Vector tests<commit_after>//===----------------------------------------------------------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is dual licensed under the MIT and the University of Illinois Open
// Source Licenses. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

// <vector>

// template <class... Args> reference emplace_back(Args&&... args);

//#include <vector>
//#include <cassert>
//#include "../../../stack_allocator.h"
//#include "min_allocator.h"
//#include "asan_testing.h"

#ifndef _LIBCPP_HAS_NO_RVALUE_REFERENCES

class A
{
    int i_;
    double d_;

    A(const A&);
    A& operator=(const A&);

public:
    A(int i, double d)
        : i_(i), d_(d) {}

    A(A&& a) = default;
    //    : i_(a.i_),
    //      d_(a.d_)
    //{
    //    a.i_ = 0;
    //    a.d_ = 0;
    //}

    A& operator=(A&& a) = default;
    //{
    //    i_ = a.i_;
    //    d_ = a.d_;
    //    a.i_ = 0;
    //    a.d_ = 0;
    //    return *this;
    //}

    int geti() const {return i_;}
    double getd() const {return d_;}
};

#endif  // _LIBCPP_HAS_NO_RVALUE_REFERENCES

void main()
{
#ifndef _LIBCPP_HAS_NO_VARIADICS
#ifndef _LIBCPP_HAS_NO_RVALUE_REFERENCES
    {
        vector<A> c;
        A& r1 = c.emplace_back(2, 3.5);
        assert(c.size() == 1);
        assert(&r1 == &c.back());
        assert(c.front().geti() == 2);
        assert(c.front().getd() == 3.5);
        //assert(is_contiguous_container_asan_correct(c));
        A& r2 = c.emplace_back(3, 4.5);
        assert(c.size() == 2);
        assert(&r2 == &c.back());
        assert(c.front().geti() == 2);
        assert(c.front().getd() == 3.5);
        assert(c.back().geti() == 3);
        assert(c.back().getd() == 4.5);
        //assert(is_contiguous_container_asan_correct(c));
    }
#ifdef LIBCPP_TEST_STACK_ALLOCATOR
    {
        vector<A, stack_allocator<A, 4> > c;
        A& r1 = c.emplace_back(2, 3.5);
        assert(c.size() == 1);
        assert(&r1 == &c.back());
        assert(c.front().geti() == 2);
        assert(c.front().getd() == 3.5);
        //assert(is_contiguous_container_asan_correct(c));
        A& r2 = c.emplace_back(3, 4.5);
        assert(c.size() == 2);
        assert(&r2 == &c.back());
        assert(c.front().geti() == 2);
        assert(c.front().getd() == 3.5);
        assert(c.back().geti() == 3);
        assert(c.back().getd() == 4.5);
        //assert(is_contiguous_container_asan_correct(c));
    }
#endif
//#if __cplusplus >= 201103L
#ifdef LIBCPP_TEST_MIN_ALLOCATOR
    {
        vector<A, min_allocator<A>> c;
        A& r1 = c.emplace_back(2, 3.5);
        assert(c.size() == 1);
        assert(&r1 == &c.back());
        assert(c.front().geti() == 2);
        assert(c.front().getd() == 3.5);
        //assert(is_contiguous_container_asan_correct(c));
        A& r2 = c.emplace_back(3, 4.5);
        assert(c.size() == 2);
        assert(&r2 == &c.back());
        assert(c.front().geti() == 2);
        assert(c.front().getd() == 3.5);
        assert(c.back().geti() == 3);
        assert(c.back().getd() == 4.5);
        //assert(is_contiguous_container_asan_correct(c));
    }
#endif
#endif  // _LIBCPP_HAS_NO_RVALUE_REFERENCES
#endif	// _LIBCPP_HAS_NO_VARIADICS
}
<|endoftext|>


<commit_before><commit_msg>Reduce limit to 2^64-1<commit_after><|endoftext|>


<commit_before>/*
* Copyright 2016 Nu-book Inc.
* Copyright 2016 ZXing authors
*
* 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 "MultiFormatReader.h"

#include "BarcodeFormat.h"
#include "DecodeHints.h"
#include "aztec/AZReader.h"
#include "datamatrix/DMReader.h"
#include "maxicode/MCReader.h"
#include "oned/ODReader.h"
#include "pdf417/PDFReader.h"
#include "qrcode/QRReader.h"

#include <memory>

namespace ZXing {

MultiFormatReader::MultiFormatReader(const DecodeHints& hints)
{
	bool tryHarder = hints.tryHarder();
	auto formats = hints.formats().empty() ? BarcodeFormat::Any : hints.formats();

	// Put 1D readers upfront in "normal" mode
	if (formats.testFlags(BarcodeFormat::OneDCodes) && !tryHarder)
		_readers.emplace_back(new OneD::Reader(hints));

	if (formats.testFlag(BarcodeFormat::QRCode))
		_readers.emplace_back(new QRCode::Reader(hints));
	if (formats.testFlag(BarcodeFormat::DataMatrix))
		_readers.emplace_back(new DataMatrix::Reader(hints));
	if (formats.testFlag(BarcodeFormat::Aztec))
		_readers.emplace_back(new Aztec::Reader(hints));
	if (formats.testFlag(BarcodeFormat::PDF417))
		_readers.emplace_back(new Pdf417::Reader(hints));
	if (formats.testFlag(BarcodeFormat::MaxiCode))
		_readers.emplace_back(new MaxiCode::Reader(hints));

	// At end in "try harder" mode
	if (formats.testFlags(BarcodeFormat::OneDCodes) && tryHarder) {
		_readers.emplace_back(new OneD::Reader(hints));
	}
}

MultiFormatReader::~MultiFormatReader() = default;

Result
MultiFormatReader::read(const BinaryBitmap& image) const
{
	// If we have only one reader in our list, just return whatever that decoded.
	// This preserves information (e.g. ChecksumError) instead of just returning 'NotFound'.
	if (_readers.size() == 1)
		return _readers.front()->decode(image);

	for (const auto& reader : _readers) {
		Result r = reader->decode(image);
  		if (r.isValid())
			return r;
	}
	return Result(DecodeStatus::NotFound);
}

Results MultiFormatReader::readMultiple(const BinaryBitmap& image, int maxSymbols) const
{
	std::vector<Result> res;

	for (const auto& reader : _readers) {
		auto r = reader->decode(image, maxSymbols);
		maxSymbols -= r.size();
		res.insert(res.end(), std::move_iterator(r.begin()), std::move_iterator(r.end()));
		if (maxSymbols <= 0)
			break;
	}

	// sort results based on their position on the image
	std::sort(res.begin(), res.end(), [](const Result& l, const Result& r) {
		auto lp = l.position().topLeft();
		auto rp = r.position().topLeft();
		return lp.y < rp.y || (lp.y == rp.y && lp.x <= rp.x);
	});

	return res;
}

} // ZXing
<commit_msg>Fixed comparison bug in MultiFormatReader.cpp<commit_after>/*
* Copyright 2016 Nu-book Inc.
* Copyright 2016 ZXing authors
*
* 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 "MultiFormatReader.h"

#include "BarcodeFormat.h"
#include "DecodeHints.h"
#include "aztec/AZReader.h"
#include "datamatrix/DMReader.h"
#include "maxicode/MCReader.h"
#include "oned/ODReader.h"
#include "pdf417/PDFReader.h"
#include "qrcode/QRReader.h"

#include <memory>

namespace ZXing {

MultiFormatReader::MultiFormatReader(const DecodeHints& hints)
{
	bool tryHarder = hints.tryHarder();
	auto formats = hints.formats().empty() ? BarcodeFormat::Any : hints.formats();

	// Put 1D readers upfront in "normal" mode
	if (formats.testFlags(BarcodeFormat::OneDCodes) && !tryHarder)
		_readers.emplace_back(new OneD::Reader(hints));

	if (formats.testFlag(BarcodeFormat::QRCode))
		_readers.emplace_back(new QRCode::Reader(hints));
	if (formats.testFlag(BarcodeFormat::DataMatrix))
		_readers.emplace_back(new DataMatrix::Reader(hints));
	if (formats.testFlag(BarcodeFormat::Aztec))
		_readers.emplace_back(new Aztec::Reader(hints));
	if (formats.testFlag(BarcodeFormat::PDF417))
		_readers.emplace_back(new Pdf417::Reader(hints));
	if (formats.testFlag(BarcodeFormat::MaxiCode))
		_readers.emplace_back(new MaxiCode::Reader(hints));

	// At end in "try harder" mode
	if (formats.testFlags(BarcodeFormat::OneDCodes) && tryHarder) {
		_readers.emplace_back(new OneD::Reader(hints));
	}
}

MultiFormatReader::~MultiFormatReader() = default;

Result
MultiFormatReader::read(const BinaryBitmap& image) const
{
	// If we have only one reader in our list, just return whatever that decoded.
	// This preserves information (e.g. ChecksumError) instead of just returning 'NotFound'.
	if (_readers.size() == 1)
		return _readers.front()->decode(image);

	for (const auto& reader : _readers) {
		Result r = reader->decode(image);
  		if (r.isValid())
			return r;
	}
	return Result(DecodeStatus::NotFound);
}

Results MultiFormatReader::readMultiple(const BinaryBitmap& image, int maxSymbols) const
{
	std::vector<Result> res;

	for (const auto& reader : _readers) {
		auto r = reader->decode(image, maxSymbols);
		maxSymbols -= r.size();
		res.insert(res.end(), std::move_iterator(r.begin()), std::move_iterator(r.end()));
		if (maxSymbols <= 0)
			break;
	}

	// sort results based on their position on the image
	std::sort(res.begin(), res.end(), [](const Result& l, const Result& r) {
		auto lp = l.position().topLeft();
		auto rp = r.position().topLeft();
		return lp.y < rp.y || (lp.y == rp.y && lp.x < rp.x);
	});

	return res;
}

} // ZXing
<|endoftext|>


<commit_before>/** \file    restart_zts
 *  \brief   Restart the docker container with the Zotero Translation Server
 *  \author  Johannes Riedl
 */

/*
    Copyright (C) 2020,2021, Library of the University of Tübingen

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

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
#include <functional>
#include <iostream>
#include <sstream>
#include "ExecUtil.h"
#include "IniFile.h"
#include "StringUtil.h"
#include "WebUtil.h"
#include "util.h"

namespace {

const std::string ZTS_RESTART_CONFIG("/usr/local/var/lib/tuelib/restart_zts.conf");
// Make sure to match this directory in /etc/sudoers.d/99-zts-restart otherwise symbolic linking will fail
const std::string ZTS_TRANSLATORS_DIR("/usr/local/zotero-translators");

void SendHeaders() {
    std::cout << "Content-Type: text/html; charset=utf-8\r\n\r\n"
              << "\n";
}

void SendTrailer() {
    std::cout << "\n";

}

struct TranslatorsLocationConfig {
    std::string name_;
    std::string url_;
    std::string local_path_;
    std::string branch_;
    TranslatorsLocationConfig(std::string name = "", std::string url = "",
        std::string local_path = "", std::string branch = "") :
        name_(name), url_(url), local_path_(local_path), branch_(branch)
        {}
};

void GetTranslatorLocationConfigs(const IniFile &ini_file,
                               std::vector<TranslatorsLocationConfig> * translators_location_configs) {
    translators_location_configs->clear();
    const std::string location_prefix("Repo_");
    for (const auto &section : ini_file) {
        if (StringUtil::StartsWith(section.getSectionName(), location_prefix)) {
            TranslatorsLocationConfig translators_location_config;
            translators_location_config.name_ = section.getSectionName().substr(location_prefix.length());
            translators_location_config.url_  = section.getString("url", "");
            translators_location_config.local_path_  = section.getString("local_path");
            translators_location_config.branch_ = section.getString("branch");
            translators_location_configs->emplace_back(translators_location_config);
        }
    }
}

bool IsRestartActionPresent(const std::multimap<std::string, std::string> &cgi_args) {
    const auto key_and_value(cgi_args.find("action"));
    return key_and_value != cgi_args.cend() and key_and_value->second == "Restart";
}

void ExecuteAndDumpMessages(const std::string &command, const std::vector<std::string> &args) {
    auto auto_temp_file((FileUtil::AutoTempFile()));
    const std::string tmp_output(auto_temp_file.getFilePath());
    ExecUtil::ExecOrDie(command, args, "" /*stdin*/, tmp_output, "/dev/stdout");
    std::ifstream output_file(tmp_output);
    if (not output_file)
        LOG_ERROR("Could not open " + tmp_output + " for reading\n");
    std::stringstream output_istream;
    output_istream << output_file.rdbuf();
    std::cout << StringUtil::ReplaceString("\n", "
", output_istream.str());
}

template<typename Function>
void ExecuteAndDisplayStatus(const std::string &header_msg, Function function, std::string footer_msg = "") {
    std::cout << header_msg << std::endl;
    bool log_no_decorations_old(logger->getLogNoDecorations());
    bool log_strip_call_site_old(logger->getLogStripCallSite());
    logger->setLogNoDecorations(true);
    logger->setLogStripCallSite(true);
    logger->redirectOutput(STDOUT_FILENO);
    try {
        function();
    } catch (const std::runtime_error &error) {
        std::cerr << error.what();
    }
    std::cout << footer_msg << std::endl;
    logger->redirectOutput(STDERR_FILENO);
    logger->setLogNoDecorations(log_no_decorations_old);
    logger->setLogStripCallSite(log_strip_call_site_old);
}

template<typename Function>
void ExecuteAndSendStatus(const std::string &message, Function function) {
    SendHeaders();
    ExecuteAndDisplayStatus(message, function);
    SendTrailer();
}

void GetCurrentRepoAndBranch() {
    const std::string chdir_to_translators_dir("cd " + ZTS_TRANSLATORS_DIR + "/translators");
    auto closure = [&]{
                      ExecuteAndDumpMessages("/usr/bin/sudo",
                       { "/bin/bash", "-c", "/usr/local/bin/restart_zts_show_current_gitrepo.sh" });
                     };
    ExecuteAndDisplayStatus("
Current repo and branch
", closure, "
");
}

void DisplayRestartAndSelectButtons(const std::vector<TranslatorsLocationConfig> &translators_location_configs) {
    SendHeaders();
    std::cout << "
Restart Zotero Translation Server Service
\n";
    GetCurrentRepoAndBranch();
    std::cout << "<form action=\"\" method=\"post\">\n";
    for (const auto &translators_location_config : translators_location_configs)
        std::cout  << "\t<input type=\"submit\" name=\"action\" value=\"" + translators_location_config.name_  +"\">\n";
    std::cout << "

" << std::endl;
    std::cout << "\t<input type=\"submit\" name=\"action\" value=\"Restart\">\n"
              << "</form>\n";
    SendTrailer();
}

void RestartZTS() {
    auto closure =  []{
                        ExecUtil::ExecOrDie("/usr/bin/sudo", { "systemctl", "restart", "zts" });
                        ExecuteAndDumpMessages("/usr/bin/sudo", { "systemctl", "status", "zts" });
                      };
    ExecuteAndSendStatus("
Trying to restart ZTS Server
", closure);
}

void RelinkTranslatorDirectory(const TranslatorsLocationConfig &translators_location_config) {
     auto closure = [&]{
                        ExecuteAndDumpMessages("/usr/bin/sudo",
                            { "ln" , "--symbolic", "--force", "--no-dereference",
                              translators_location_config.local_path_, ZTS_TRANSLATORS_DIR});
                        std::cout << "Linking " + ZTS_TRANSLATORS_DIR + " to " + translators_location_config.local_path_ << '\n';
                        RestartZTS();
                       };
     ExecuteAndSendStatus("
Switching to branch " + translators_location_config.name_ + "
",
                           closure);
}

bool GetSwitchBranch(const std::multimap<std::string, std::string> &cgi_args,
                     std::vector<TranslatorsLocationConfig> translators_location_configs,
                     TranslatorsLocationConfig * const translator_location_config) {
     const auto key_and_value(cgi_args.find("action"));
     if (key_and_value == cgi_args.end())
         return false;
     const std::string target(key_and_value->second);
     auto match(std::find_if(translators_location_configs.begin(),
                             translators_location_configs.end(),
                             [&target](const TranslatorsLocationConfig &target_obj)
                                      {return target_obj.name_ == target;}));
     if (match == translators_location_configs.end()) {
         std::cout << "NO MATCH";
         return false;
     }
     *translator_location_config = *match;
     return true;
}

} // end unnamed namespace

int Main(int /*argc*/, char */*argv*/[]) {
    std::multimap<std::string, std::string> cgi_args;
    WebUtil::GetAllCgiArgs(&cgi_args);
    IniFile ini_file(ZTS_RESTART_CONFIG);
    std::vector<TranslatorsLocationConfig> translators_location_configs;
    GetTranslatorLocationConfigs(ini_file, &translators_location_configs);
    if (IsRestartActionPresent(cgi_args)) {
        RestartZTS();
        return EXIT_SUCCESS;
    }

    TranslatorsLocationConfig translators_location_config;
    if (GetSwitchBranch(cgi_args, translators_location_configs, &translators_location_config)) {
        RelinkTranslatorDirectory(translators_location_config);
        return EXIT_SUCCESS;
    }

    DisplayRestartAndSelectButtons(translators_location_configs);
    return EXIT_SUCCESS;
}

<commit_msg>ZEM switch functionality<commit_after>/** \file    restart_zts
 *  \brief   Restart the docker container with the Zotero Translation Server
 *  \author  Johannes Riedl
 */

/*
    Copyright (C) 2020,2021, Library of the University of Tübingen

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

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
#include <functional>
#include <iostream>
#include <sstream>
#include "ExecUtil.h"
#include "IniFile.h"
#include "StringUtil.h"
#include "WebUtil.h"
#include "util.h"

namespace {

const std::string ZTS_RESTART_CONFIG("/usr/local/var/lib/tuelib/restart_zts.conf");
// Make sure to match this directory in /etc/sudoers.d/99-zts-restart otherwise symbolic linking will fail
const std::string ZTS_TRANSLATORS_DIR("/usr/local/zotero-translators");
const std::string ZOTERO_ENHANCEMENT_MAPS_DIR("/usr/local/var/lib/tuelib/zotero-enhancement-maps");

void SendHeaders() {
    std::cout << "Content-Type: text/html; charset=utf-8\r\n\r\n"
              << "\n";
}

void SendTrailer() {
    std::cout << "\n";

}

struct TranslatorsLocationConfig {
    std::string name_;
    std::string url_;
    std::string local_path_;
    std::string branch_;
    std::string zotero_enhancement_maps_local_path_;
    std::string zotero_enhancement_maps_branch_;
    TranslatorsLocationConfig(std::string name = "", std::string url = "",
        std::string local_path = "", std::string branch = "",
        std::string zotero_enhancement_maps_local_path = "",
        std::string zotero_enhancement_maps_branch = "") :
        name_(name), url_(url), local_path_(local_path), branch_(branch),
        zotero_enhancement_maps_local_path_(zotero_enhancement_maps_local_path),
        zotero_enhancement_maps_branch_(zotero_enhancement_maps_branch)
        {}
};

void GetTranslatorLocationConfigs(const IniFile &ini_file,
                               std::vector<TranslatorsLocationConfig> * translators_location_configs) {
    translators_location_configs->clear();
    const std::string location_prefix("Repo_");
    for (const auto &section : ini_file) {
        if (StringUtil::StartsWith(section.getSectionName(), location_prefix)) {
            TranslatorsLocationConfig translators_location_config;
            translators_location_config.name_ = section.getSectionName().substr(location_prefix.length());
            translators_location_config.url_  = section.getString("url", "");
            translators_location_config.local_path_  = section.getString("local_path");
            translators_location_config.branch_ = section.getString("branch");
            translators_location_config.zotero_enhancement_maps_local_path_ =
                section.getString("zotero_enhancement_maps_local_path");
            translators_location_config.zotero_enhancement_maps_branch_ =
                section.getString("zotero_enhancement_maps_branch");
            translators_location_configs->emplace_back(translators_location_config);
        }
    }
}

bool IsRestartActionPresent(const std::multimap<std::string, std::string> &cgi_args) {
    const auto key_and_value(cgi_args.find("action"));
    return key_and_value != cgi_args.cend() and key_and_value->second == "Restart";
}

void ExecuteAndDumpMessages(const std::string &command, const std::vector<std::string> &args) {
    auto auto_temp_file((FileUtil::AutoTempFile()));
    const std::string tmp_output(auto_temp_file.getFilePath());
    ExecUtil::ExecOrDie(command, args, "" /*stdin*/, tmp_output, "/dev/stdout");
    std::ifstream output_file(tmp_output);
    if (not output_file)
        LOG_ERROR("Could not open " + tmp_output + " for reading\n");
    std::stringstream output_istream;
    output_istream << output_file.rdbuf();
    std::cout << StringUtil::ReplaceString("\n", "
", output_istream.str());
}

template<typename Function>
void ExecuteAndDisplayStatus(const std::string &header_msg, Function function, std::string footer_msg = "") {
    std::cout << header_msg << std::endl;
    bool log_no_decorations_old(logger->getLogNoDecorations());
    bool log_strip_call_site_old(logger->getLogStripCallSite());
    logger->setLogNoDecorations(true);
    logger->setLogStripCallSite(true);
    logger->redirectOutput(STDOUT_FILENO);
    try {
        function();
    } catch (const std::runtime_error &error) {
        std::cerr << error.what();
    }
    std::cout << footer_msg << std::endl;
    logger->redirectOutput(STDERR_FILENO);
    logger->setLogNoDecorations(log_no_decorations_old);
    logger->setLogStripCallSite(log_strip_call_site_old);
}

template<typename Function>
void ExecuteAndSendStatus(const std::string &message, Function function) {
    SendHeaders();
    ExecuteAndDisplayStatus(message, function);
    SendTrailer();
}

void GetCurrentRepoAndBranch() {
    const std::string chdir_to_translators_dir("cd " + ZTS_TRANSLATORS_DIR + "/translators");
    auto closure = [&]{
                      ExecuteAndDumpMessages("/usr/bin/sudo",
                       { "/bin/bash", "-c", "/usr/local/bin/restart_zts_show_current_gitrepo.sh" });
                     };
    ExecuteAndDisplayStatus("
Current repo and branch
", closure, "
");
}

void DisplayRestartAndSelectButtons(const std::vector<TranslatorsLocationConfig> &translators_location_configs) {
    SendHeaders();
    std::cout << "
Restart Zotero Translation Server Service
\n";
    GetCurrentRepoAndBranch();
    std::cout << "<form action=\"\" method=\"post\">\n";
    for (const auto &translators_location_config : translators_location_configs)
        std::cout  << "\t<input type=\"submit\" name=\"action\" value=\"" + translators_location_config.name_  +"\">\n";
    std::cout << "

" << std::endl;
    std::cout << "\t<input type=\"submit\" name=\"action\" value=\"Restart\">\n"
              << "</form>\n";
    SendTrailer();
}

void RestartZTS() {
    auto closure =  []{
                        ExecUtil::ExecOrDie("/usr/bin/sudo", { "systemctl", "restart", "zts" });
                        ExecuteAndDumpMessages("/usr/bin/sudo", { "systemctl", "status", "zts" });
                      };
    ExecuteAndSendStatus("
Trying to restart ZTS Server
", closure);
}

void RelinkTranslatorAndEnhancemenMapsDirectory(const TranslatorsLocationConfig &translators_location_config) {
     auto closure = [&]{
                        ExecuteAndDumpMessages("/usr/bin/sudo",
                            { "ln" , "--symbolic", "--force", "--no-dereference",
                              translators_location_config.local_path_, ZTS_TRANSLATORS_DIR});
                        std::cout << "Linking " << ZTS_TRANSLATORS_DIR << " to " << translators_location_config.local_path_ << "
";
                        ExecuteAndDumpMessages("/usr/bin/sudo",
                            { "ln" , "--symbolic", "--force", "--no-dereference",
                              translators_location_config.zotero_enhancement_maps_local_path_, ZOTERO_ENHANCEMENT_MAPS_DIR});
                        std::cout << "Linking " << ZOTERO_ENHANCEMENT_MAPS_DIR << " to " <<
                                     translators_location_config.zotero_enhancement_maps_local_path_ << "
";
                        RestartZTS();
                       };
     ExecuteAndSendStatus("
Switching to branch " + translators_location_config.name_ + "
",
                           closure);
}

bool GetSwitchBranch(const std::multimap<std::string, std::string> &cgi_args,
                     std::vector<TranslatorsLocationConfig> translators_location_configs,
                     TranslatorsLocationConfig * const translator_location_config) {
     const auto key_and_value(cgi_args.find("action"));
     if (key_and_value == cgi_args.end())
         return false;
     const std::string target(key_and_value->second);
     auto match(std::find_if(translators_location_configs.begin(),
                             translators_location_configs.end(),
                             [&target](const TranslatorsLocationConfig &target_obj)
                                      {return target_obj.name_ == target;}));
     if (match == translators_location_configs.end()) {
         std::cout << "NO MATCH";
         return false;
     }
     *translator_location_config = *match;
     return true;
}

} // end unnamed namespace

int Main(int /*argc*/, char */*argv*/[]) {
    std::multimap<std::string, std::string> cgi_args;
    WebUtil::GetAllCgiArgs(&cgi_args);
    IniFile ini_file(ZTS_RESTART_CONFIG);
    std::vector<TranslatorsLocationConfig> translators_location_configs;
    GetTranslatorLocationConfigs(ini_file, &translators_location_configs);
    if (IsRestartActionPresent(cgi_args)) {
        RestartZTS();
        return EXIT_SUCCESS;
    }

    TranslatorsLocationConfig translators_location_config;
    if (GetSwitchBranch(cgi_args, translators_location_configs, &translators_location_config)) {
        RelinkTranslatorAndEnhancemenMapsDirectory(translators_location_config);
        return EXIT_SUCCESS;
    }

    DisplayRestartAndSelectButtons(translators_location_configs);
    return EXIT_SUCCESS;
}

<|endoftext|>


<commit_before>/** \file    Elasticsearch.cc
 *  \brief   Implementation of utility functions relating to Elasticsearch.
 *  \author  Mario Trojan
 *
 *  \copyright 2018 Universitätsbibliothek Tübingen.  All rights reserved.
 *
 *  This program 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.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
#include "Elasticsearch.h"
#include <memory>
#include "FileUtil.h"
#include "IniFile.h"

std::shared_ptr<Elasticsearch::Configuration> Elasticsearch::Configuration::FactoryByConfigFile() {
    const std::string ini_path("/usr/local/var/lib/tuelib/Elasticsearch.conf");
    if (not FileUtil::Exists(ini_path))
        ERROR("config file missing: " + ini_path);

    const IniFile ini_file(ini_path);

    std::shared_ptr<Elasticsearch::Configuration> config(new Elasticsearch::Configuration);
    config->host_ = Url(ini_file.getString("Elasticsearch", "host"));
    config->index_ = ini_file.getString("Elasticsearch", "index");
    config->document_type_ = ini_file.getString("Elasticsearch", "document_type");
    return config;
}

std::shared_ptr<JSON::ObjectNode> FieldsToJSON(const Elasticsearch::Fields &fields) {
    std::shared_ptr<JSON::ObjectNode> tree_root(new JSON::ObjectNode);
    for (const auto &field : fields) {
        std::shared_ptr<JSON::StringNode> value_node(new JSON::StringNode(field.second));
        tree_root->insert(field.first, value_node);
    }
    return tree_root;
}

Elasticsearch::Fields JSONToFields(const std::shared_ptr<const JSON::ObjectNode> &json_object) {
    Elasticsearch::Fields fields;
    for (const auto &key_and_value : *json_object) {
        std::shared_ptr<JSON::StringNode> string_node(JSON::JSONNode::CastToStringNodeOrDie(key_and_value.first, key_and_value.second));
        fields[key_and_value.first] = string_node->getValue();
    }
    return fields;
}

/** \brief Sends REST queries to Elasticsearch server
*   \throws std::runtime_error on REST error or if the JSON response contained an error flag.
*/
std::shared_ptr<JSON::ObjectNode> Elasticsearch::Api::Query(const Url &host, const std::string &action, const REST::QueryType query_type, const std::shared_ptr<const JSON::JSONNode> &data) {
    Url url(host.toString() + "/" + action);
    Downloader::Params params;
    if (data != nullptr)
        params.additional_headers_.push_back("Content-Type: application/json");
    std::shared_ptr<JSON::JSONNode> result(REST::QueryJSON(url, query_type, data, params));
    std::shared_ptr<JSON::ObjectNode> result_object(JSON::JSONNode::CastToObjectNodeOrDie("Elasticsearch result", result));
    if (result_object->hasNode("error"))
        throw std::runtime_error("in Elasticsearch::query: " + result_object->getNode("error")->toString());

    DEBUG(result_object->toString());

    return result_object;
}

void Elasticsearch::Api::CreateDocument(const Url &host, const std::string &index, const Document &document) {
    const std::shared_ptr<JSON::ObjectNode> tree_root(FieldsToJSON(document.fields_));
    const std::string action(index + "/" + document.type_ + "/" + document.id_ + "?op_type=create");
    Query(host, action, REST::QueryType::PUT, tree_root);
}

void Elasticsearch::Api::CreateIndex(const Url &host, const std::string &index) {
    Query(host, index, REST::QueryType::PUT);
}

void Elasticsearch::Api::DeleteDocument(const Url &host, const std::string &index, const std::string &type, const std::string &id) {
    const std::string action(index + "/" + type + "/" + id);
    Query(host, action, REST::QueryType::DELETE);
}

void Elasticsearch::Api::DeleteIndex(const Url &host, const std::string &index) {
    Query(host, index, REST::QueryType::DELETE);
}

Elasticsearch::Document Elasticsearch::Api::GetDocument(const Url &host, const std::string &index, const std::string &type, const std::string &id) {
    const std::string action(index + "/" + type + "/" + id);
    std::shared_ptr<JSON::ObjectNode> result(Query(host, action, REST::QueryType::GET));
    bool found(result->getOptionalBooleanValue("found", false));
    if (not found)
        throw std::runtime_error("in Elasticsearch::getDocument: document not found!" + result->toString());

    Document document;
    document.id_ = id;
    document.fields_ = JSONToFields(result->getObjectNode("_source"));
    return document;
}

std::vector<std::string> Elasticsearch::Api::GetIndexList(const Url &host) {
    const std::string action("_cluster/health?level=indices");
    const std::shared_ptr<const JSON::ObjectNode> result_node(Query(host, action, REST::QueryType::GET));

    if (not result_node->hasNode("indices"))
        throw std::runtime_error("in Elasticsearch::getIndexList: indices key not found in result: " + result_node->toString());

    const std::shared_ptr<const JSON::ObjectNode> index_list_node(result_node->getObjectNode("indices"));
    std::vector<std::string> index_list;
    for (const auto &key_and_node : *index_list_node)
        index_list.push_back(key_and_node.first);

    return index_list;
}

Elasticsearch::IndexStatistics Elasticsearch::Api::GetIndexStatistics(const Url &host, const std::string &index) {
    const std::string action(index + "/_stats");
    const std::shared_ptr<const JSON::ObjectNode> result_object(Query(host, action, REST::QueryType::GET));
    const std::shared_ptr<const JSON::ObjectNode> indices_object(result_object->getObjectNode("indices"));
    const std::shared_ptr<const JSON::ObjectNode> index_object(indices_object->getObjectNode(index));
    const std::shared_ptr<const JSON::ObjectNode> total_object(index_object->getObjectNode("total"));
    const std::shared_ptr<const JSON::ObjectNode> docs_object(total_object->getObjectNode("docs"));
    IndexStatistics stats;
    stats.document_count_ = static_cast<unsigned>(docs_object->getIntegerValue("count"));
    return stats;
}

bool Elasticsearch::Api::HasDocument(const Url &host, const std::string &index, const std::string &type, const std::string &id) {
    const std::string action(index + "/" + type + "/" + id);
    const std::shared_ptr<const JSON::ObjectNode> result(Query(host, action, REST::QueryType::GET));
    return result->getOptionalBooleanValue("found", false);
}

void Elasticsearch::Api::Reindex(const Url &host, const std::string &source_index, const std::string &target_index) {
    std::shared_ptr<JSON::ObjectNode> tree_root(new JSON::ObjectNode);

    std::shared_ptr<JSON::ObjectNode> source_node(new JSON::ObjectNode);
    std::shared_ptr<JSON::StringNode> source_index_node(new JSON::StringNode(source_index));
    source_node->insert("index", source_index_node);
    tree_root->insert("source", source_node);

    std::shared_ptr<JSON::ObjectNode> dest_node(new JSON::ObjectNode);
    std::shared_ptr<JSON::StringNode> dest_index_node(new JSON::StringNode(target_index));
    dest_node->insert("index", dest_index_node);
    tree_root->insert("dest", dest_node);

    Query(host, "_reindex", REST::QueryType::POST, tree_root);
}

Elasticsearch::IdToDocumentMap Elasticsearch::Api::SearchAllDocuments(const Url &host, const std::string &index) {
    const std::string action(index + "/_search");

    const std::shared_ptr<JSON::ObjectNode> tree_root(new JSON::ObjectNode);
    std::shared_ptr<JSON::ObjectNode> query_node(new JSON::ObjectNode);
    tree_root->insert("query", query_node);
    std::shared_ptr<JSON::ObjectNode> match_all_node(new JSON::ObjectNode);
    query_node->insert("match_all", match_all_node);

    const std::shared_ptr<const JSON::ObjectNode> result_node(Query(host, action, REST::QueryType::GET, tree_root));

    IdToDocumentMap documents;
    if (result_node->hasNode("hits")) {
        const std::shared_ptr<const JSON::ObjectNode> hits_object(result_node->getObjectNode("hits"));
        const std::shared_ptr<const JSON::ArrayNode> hits_array(hits_object->getArrayNode("hits"));
        for (const auto &hit_node : *hits_array) {
            const std::shared_ptr<const JSON::ObjectNode> hit_object(JSON::JSONNode::CastToObjectNodeOrDie("hit", hit_node));
            Document document;
            document.id_ = hit_object->getStringValue("_id");
            if (hit_object->hasNode("_source")) {
                const std::shared_ptr<const JSON::ObjectNode> fields_object(hit_object->getObjectNode("_source"));
                document.fields_ = JSONToFields(fields_object);
            }
            documents[document.id_] = document;
        }
    }
    return documents;
}

void Elasticsearch::Api::UpdateDocument(const Url &host, const std::string &index, const Document &document) {
    std::shared_ptr<JSON::ObjectNode> doc_node(FieldsToJSON(document.fields_));
    std::shared_ptr<JSON::ObjectNode> tree_root(new JSON::ObjectNode);
    tree_root->insert("doc", doc_node);

    const std::string action(index + "/" + document.type_ + "/" + document.id_ + "/_update");
    Query(host, action, REST::QueryType::POST, tree_root);
}

void Elasticsearch::Api::UpdateOrInsertDocument(const Url &host, const std::string &index, const Document &document) {
    std::shared_ptr<JSON::ObjectNode> tree_root(new JSON::ObjectNode);
    std::shared_ptr<JSON::ObjectNode> doc_node(FieldsToJSON(document.fields_));
    tree_root->insert("doc", doc_node);
    std::shared_ptr<JSON::BooleanNode> doc_as_upsert_node(new JSON::BooleanNode(true));
    tree_root->insert("doc_as_upsert", doc_as_upsert_node);

    const std::string action(index + "/" + document.type_ + "/" + document.id_ + "/_update");
    Query(host, action, REST::QueryType::POST, tree_root);
}
<commit_msg>Elasticsearch: fixed GetDocument => type not being set<commit_after>/** \file    Elasticsearch.cc
 *  \brief   Implementation of utility functions relating to Elasticsearch.
 *  \author  Mario Trojan
 *
 *  \copyright 2018 Universitätsbibliothek Tübingen.  All rights reserved.
 *
 *  This program 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.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
#include "Elasticsearch.h"
#include <memory>
#include "FileUtil.h"
#include "IniFile.h"

std::shared_ptr<Elasticsearch::Configuration> Elasticsearch::Configuration::FactoryByConfigFile() {
    const std::string ini_path("/usr/local/var/lib/tuelib/Elasticsearch.conf");
    if (not FileUtil::Exists(ini_path))
        ERROR("config file missing: " + ini_path);

    const IniFile ini_file(ini_path);

    std::shared_ptr<Elasticsearch::Configuration> config(new Elasticsearch::Configuration);
    config->host_ = Url(ini_file.getString("Elasticsearch", "host"));
    config->index_ = ini_file.getString("Elasticsearch", "index");
    config->document_type_ = ini_file.getString("Elasticsearch", "document_type");
    return config;
}

std::shared_ptr<JSON::ObjectNode> FieldsToJSON(const Elasticsearch::Fields &fields) {
    std::shared_ptr<JSON::ObjectNode> tree_root(new JSON::ObjectNode);
    for (const auto &field : fields) {
        std::shared_ptr<JSON::StringNode> value_node(new JSON::StringNode(field.second));
        tree_root->insert(field.first, value_node);
    }
    return tree_root;
}

Elasticsearch::Fields JSONToFields(const std::shared_ptr<const JSON::ObjectNode> &json_object) {
    Elasticsearch::Fields fields;
    for (const auto &key_and_value : *json_object) {
        std::shared_ptr<JSON::StringNode> string_node(JSON::JSONNode::CastToStringNodeOrDie(key_and_value.first, key_and_value.second));
        fields[key_and_value.first] = string_node->getValue();
    }
    return fields;
}

/** \brief Sends REST queries to Elasticsearch server
*   \throws std::runtime_error on REST error or if the JSON response contained an error flag.
*/
std::shared_ptr<JSON::ObjectNode> Elasticsearch::Api::Query(const Url &host, const std::string &action, const REST::QueryType query_type, const std::shared_ptr<const JSON::JSONNode> &data) {
    Url url(host.toString() + "/" + action);
    Downloader::Params params;
    if (data != nullptr)
        params.additional_headers_.push_back("Content-Type: application/json");
    std::shared_ptr<JSON::JSONNode> result(REST::QueryJSON(url, query_type, data, params));
    std::shared_ptr<JSON::ObjectNode> result_object(JSON::JSONNode::CastToObjectNodeOrDie("Elasticsearch result", result));
    if (result_object->hasNode("error"))
        throw std::runtime_error("in Elasticsearch::query: " + result_object->getNode("error")->toString());

    DEBUG(result_object->toString());

    return result_object;
}

void Elasticsearch::Api::CreateDocument(const Url &host, const std::string &index, const Document &document) {
    const std::shared_ptr<JSON::ObjectNode> tree_root(FieldsToJSON(document.fields_));
    const std::string action(index + "/" + document.type_ + "/" + document.id_ + "?op_type=create");
    Query(host, action, REST::QueryType::PUT, tree_root);
}

void Elasticsearch::Api::CreateIndex(const Url &host, const std::string &index) {
    Query(host, index, REST::QueryType::PUT);
}

void Elasticsearch::Api::DeleteDocument(const Url &host, const std::string &index, const std::string &type, const std::string &id) {
    const std::string action(index + "/" + type + "/" + id);
    Query(host, action, REST::QueryType::DELETE);
}

void Elasticsearch::Api::DeleteIndex(const Url &host, const std::string &index) {
    Query(host, index, REST::QueryType::DELETE);
}

Elasticsearch::Document Elasticsearch::Api::GetDocument(const Url &host, const std::string &index, const std::string &type, const std::string &id) {
    const std::string action(index + "/" + type + "/" + id);
    std::shared_ptr<JSON::ObjectNode> result(Query(host, action, REST::QueryType::GET));
    bool found(result->getOptionalBooleanValue("found", false));
    if (not found)
        throw std::runtime_error("in Elasticsearch::getDocument: document not found!" + result->toString());

    Document document;
    document.id_ = id;
    document.type_ = type;
    document.fields_ = JSONToFields(result->getObjectNode("_source"));
    return document;
}

std::vector<std::string> Elasticsearch::Api::GetIndexList(const Url &host) {
    const std::string action("_cluster/health?level=indices");
    const std::shared_ptr<const JSON::ObjectNode> result_node(Query(host, action, REST::QueryType::GET));

    if (not result_node->hasNode("indices"))
        throw std::runtime_error("in Elasticsearch::getIndexList: indices key not found in result: " + result_node->toString());

    const std::shared_ptr<const JSON::ObjectNode> index_list_node(result_node->getObjectNode("indices"));
    std::vector<std::string> index_list;
    for (const auto &key_and_node : *index_list_node)
        index_list.push_back(key_and_node.first);

    return index_list;
}

Elasticsearch::IndexStatistics Elasticsearch::Api::GetIndexStatistics(const Url &host, const std::string &index) {
    const std::string action(index + "/_stats");
    const std::shared_ptr<const JSON::ObjectNode> result_object(Query(host, action, REST::QueryType::GET));
    const std::shared_ptr<const JSON::ObjectNode> indices_object(result_object->getObjectNode("indices"));
    const std::shared_ptr<const JSON::ObjectNode> index_object(indices_object->getObjectNode(index));
    const std::shared_ptr<const JSON::ObjectNode> total_object(index_object->getObjectNode("total"));
    const std::shared_ptr<const JSON::ObjectNode> docs_object(total_object->getObjectNode("docs"));
    IndexStatistics stats;
    stats.document_count_ = static_cast<unsigned>(docs_object->getIntegerValue("count"));
    return stats;
}

bool Elasticsearch::Api::HasDocument(const Url &host, const std::string &index, const std::string &type, const std::string &id) {
    const std::string action(index + "/" + type + "/" + id);
    const std::shared_ptr<const JSON::ObjectNode> result(Query(host, action, REST::QueryType::GET));
    return result->getOptionalBooleanValue("found", false);
}

void Elasticsearch::Api::Reindex(const Url &host, const std::string &source_index, const std::string &target_index) {
    std::shared_ptr<JSON::ObjectNode> tree_root(new JSON::ObjectNode);

    std::shared_ptr<JSON::ObjectNode> source_node(new JSON::ObjectNode);
    std::shared_ptr<JSON::StringNode> source_index_node(new JSON::StringNode(source_index));
    source_node->insert("index", source_index_node);
    tree_root->insert("source", source_node);

    std::shared_ptr<JSON::ObjectNode> dest_node(new JSON::ObjectNode);
    std::shared_ptr<JSON::StringNode> dest_index_node(new JSON::StringNode(target_index));
    dest_node->insert("index", dest_index_node);
    tree_root->insert("dest", dest_node);

    Query(host, "_reindex", REST::QueryType::POST, tree_root);
}

Elasticsearch::IdToDocumentMap Elasticsearch::Api::SearchAllDocuments(const Url &host, const std::string &index) {
    const std::string action(index + "/_search");

    const std::shared_ptr<JSON::ObjectNode> tree_root(new JSON::ObjectNode);
    std::shared_ptr<JSON::ObjectNode> query_node(new JSON::ObjectNode);
    tree_root->insert("query", query_node);
    std::shared_ptr<JSON::ObjectNode> match_all_node(new JSON::ObjectNode);
    query_node->insert("match_all", match_all_node);

    const std::shared_ptr<const JSON::ObjectNode> result_node(Query(host, action, REST::QueryType::GET, tree_root));

    IdToDocumentMap documents;
    if (result_node->hasNode("hits")) {
        const std::shared_ptr<const JSON::ObjectNode> hits_object(result_node->getObjectNode("hits"));
        const std::shared_ptr<const JSON::ArrayNode> hits_array(hits_object->getArrayNode("hits"));
        for (const auto &hit_node : *hits_array) {
            const std::shared_ptr<const JSON::ObjectNode> hit_object(JSON::JSONNode::CastToObjectNodeOrDie("hit", hit_node));
            Document document;
            document.id_ = hit_object->getStringValue("_id");
            if (hit_object->hasNode("_source")) {
                const std::shared_ptr<const JSON::ObjectNode> fields_object(hit_object->getObjectNode("_source"));
                document.fields_ = JSONToFields(fields_object);
            }
            documents[document.id_] = document;
        }
    }
    return documents;
}

void Elasticsearch::Api::UpdateDocument(const Url &host, const std::string &index, const Document &document) {
    std::shared_ptr<JSON::ObjectNode> doc_node(FieldsToJSON(document.fields_));
    std::shared_ptr<JSON::ObjectNode> tree_root(new JSON::ObjectNode);
    tree_root->insert("doc", doc_node);

    const std::string action(index + "/" + document.type_ + "/" + document.id_ + "/_update");
    Query(host, action, REST::QueryType::POST, tree_root);
}

void Elasticsearch::Api::UpdateOrInsertDocument(const Url &host, const std::string &index, const Document &document) {
    std::shared_ptr<JSON::ObjectNode> tree_root(new JSON::ObjectNode);
    std::shared_ptr<JSON::ObjectNode> doc_node(FieldsToJSON(document.fields_));
    tree_root->insert("doc", doc_node);
    std::shared_ptr<JSON::BooleanNode> doc_as_upsert_node(new JSON::BooleanNode(true));
    tree_root->insert("doc_as_upsert", doc_as_upsert_node);

    const std::string action(index + "/" + document.type_ + "/" + document.id_ + "/_update");
    Query(host, action, REST::QueryType::POST, tree_root);
}
<|endoftext|>