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

#include "config.h"
#include "modules/filesystem/FileSystemCallbacks.h"

#include "core/dom/ExecutionContext.h"
#include "core/fileapi/FileError.h"
#include "core/html/VoidCallback.h"
#include "modules/filesystem/DOMFilePath.h"
#include "modules/filesystem/DOMFileSystemBase.h"
#include "modules/filesystem/DirectoryEntry.h"
#include "modules/filesystem/DirectoryReader.h"
#include "modules/filesystem/Entry.h"
#include "modules/filesystem/EntryCallback.h"
#include "modules/filesystem/ErrorCallback.h"
#include "modules/filesystem/FileEntry.h"
#include "modules/filesystem/FileSystemCallback.h"
#include "modules/filesystem/FileWriterBase.h"
#include "modules/filesystem/FileWriterBaseCallback.h"
#include "modules/filesystem/Metadata.h"
#include "modules/filesystem/MetadataCallback.h"
#include "platform/FileMetadata.h"
#include "public/platform/WebFileWriter.h"

namespace WebCore {

FileSystemCallbacksBase::FileSystemCallbacksBase(PassRefPtr<ErrorCallback> errorCallback, DOMFileSystemBase* fileSystem)
    : m_errorCallback(errorCallback)
    , m_fileSystem(fileSystem)
{
    if (m_fileSystem)
        m_fileSystem->addPendingCallbacks();
}

FileSystemCallbacksBase::~FileSystemCallbacksBase()
{
    if (m_fileSystem)
        m_fileSystem->removePendingCallbacks();
}

void FileSystemCallbacksBase::didFail(int code)
{
    if (m_errorCallback) {
        m_errorCallback->handleEvent(FileError::create(static_cast<FileError::ErrorCode>(code)).get());
        m_errorCallback.clear();
    }
}

// EntryCallbacks -------------------------------------------------------------

PassOwnPtr<AsyncFileSystemCallbacks> EntryCallbacks::create(PassRefPtr<EntryCallback> successCallback, PassRefPtr<ErrorCallback> errorCallback, PassRefPtr<DOMFileSystemBase> fileSystem, const String& expectedPath, bool isDirectory)
{
    return adoptPtr(static_cast<AsyncFileSystemCallbacks*>(new EntryCallbacks(successCallback, errorCallback, fileSystem, expectedPath, isDirectory)));
}

EntryCallbacks::EntryCallbacks(PassRefPtr<EntryCallback> successCallback, PassRefPtr<ErrorCallback> errorCallback, PassRefPtr<DOMFileSystemBase> fileSystem, const String& expectedPath, bool isDirectory)
    : FileSystemCallbacksBase(errorCallback, fileSystem.get())
    , m_successCallback(successCallback)
    , m_expectedPath(expectedPath)
    , m_isDirectory(isDirectory)
{
}

void EntryCallbacks::didSucceed()
{
    if (m_successCallback) {
        if (m_isDirectory)
            m_successCallback->handleEvent(DirectoryEntry::create(m_fileSystem, m_expectedPath).get());
        else
            m_successCallback->handleEvent(FileEntry::create(m_fileSystem, m_expectedPath).get());
    }
    m_successCallback.clear();
}

// EntriesCallbacks -----------------------------------------------------------

PassOwnPtr<AsyncFileSystemCallbacks> EntriesCallbacks::create(PassRefPtr<EntriesCallback> successCallback, PassRefPtr<ErrorCallback> errorCallback, PassRefPtr<DirectoryReaderBase> directoryReader, const String& basePath)
{
    return adoptPtr(static_cast<AsyncFileSystemCallbacks*>(new EntriesCallbacks(successCallback, errorCallback, directoryReader, basePath)));
}

EntriesCallbacks::EntriesCallbacks(PassRefPtr<EntriesCallback> successCallback, PassRefPtr<ErrorCallback> errorCallback, PassRefPtr<DirectoryReaderBase> directoryReader, const String& basePath)
    : FileSystemCallbacksBase(errorCallback, directoryReader->filesystem())
    , m_successCallback(successCallback)
    , m_directoryReader(directoryReader)
    , m_basePath(basePath)
{
    ASSERT(m_directoryReader);
}

void EntriesCallbacks::didReadDirectoryEntry(const String& name, bool isDirectory)
{
    if (isDirectory)
        m_entries.append(DirectoryEntry::create(m_directoryReader->filesystem(), DOMFilePath::append(m_basePath, name)));
    else
        m_entries.append(FileEntry::create(m_directoryReader->filesystem(), DOMFilePath::append(m_basePath, name)));
}

void EntriesCallbacks::didReadDirectoryEntries(bool hasMore)
{
    m_directoryReader->setHasMoreEntries(hasMore);
    if (m_successCallback)
        m_successCallback->handleEvent(m_entries);
}

// FileSystemCallbacks --------------------------------------------------------

PassOwnPtr<AsyncFileSystemCallbacks> FileSystemCallbacks::create(PassRefPtr<FileSystemCallback> successCallback, PassRefPtr<ErrorCallback> errorCallback, ExecutionContext* executionContext, FileSystemType type)
{
    return adoptPtr(static_cast<AsyncFileSystemCallbacks*>(new FileSystemCallbacks(successCallback, errorCallback, executionContext, type)));
}

FileSystemCallbacks::FileSystemCallbacks(PassRefPtr<FileSystemCallback> successCallback, PassRefPtr<ErrorCallback> errorCallback, ExecutionContext* context, FileSystemType type)
    : FileSystemCallbacksBase(errorCallback, 0)
    , m_successCallback(successCallback)
    , m_executionContext(context)
    , m_type(type)
{
}

void FileSystemCallbacks::didOpenFileSystem(const String& name, const KURL& rootURL)
{
    if (m_successCallback) {
        RefPtr<DOMFileSystem> fileSystem = DOMFileSystem::create(m_executionContext.get(), name, m_type, rootURL);
        m_successCallback->handleEvent(fileSystem.get());
        m_executionContext.clear();
    }
    m_successCallback.clear();
}

// ResolveURICallbacks --------------------------------------------------------

namespace {

class ErrorCallbackWrapper : public ErrorCallback {
public:
    static PassRefPtr<ErrorCallbackWrapper> create(PassRefPtr<EntryCallback> successCallback, PassRefPtr<ErrorCallback> errorCallback, PassRefPtr<DirectoryEntry> root, const String& filePath)
    {
        return adoptRef(new ErrorCallbackWrapper(successCallback, errorCallback, root, filePath));
    }

    virtual bool handleEvent(FileError* error)
    {
        ASSERT(error);
        if (error->code() == FileError::TYPE_MISMATCH_ERR)
            m_root->getFile(m_filePath, Dictionary(), m_successCallback, m_errorCallback);
        else if (m_errorCallback)
            m_errorCallback->handleEvent(error);
        return true;
    }

private:
    ErrorCallbackWrapper(PassRefPtr<EntryCallback> successCallback, PassRefPtr<ErrorCallback> errorCallback, PassRefPtr<DirectoryEntry> root, const String& filePath)
        : m_successCallback(successCallback)
        , m_errorCallback(errorCallback)
        , m_root(root)
        , m_filePath(filePath)
    {
        ASSERT(m_root);
    }

    RefPtr<EntryCallback> m_successCallback;
    RefPtr<ErrorCallback> m_errorCallback;
    RefPtr<DirectoryEntry> m_root;
    String m_filePath;
};

} // namespace

PassOwnPtr<AsyncFileSystemCallbacks> ResolveURICallbacks::create(PassRefPtr<EntryCallback> successCallback, PassRefPtr<ErrorCallback> errorCallback, ExecutionContext* executionContext)
{
    return adoptPtr(static_cast<AsyncFileSystemCallbacks*>(new ResolveURICallbacks(successCallback, errorCallback, executionContext)));
}

ResolveURICallbacks::ResolveURICallbacks(PassRefPtr<EntryCallback> successCallback, PassRefPtr<ErrorCallback> errorCallback, ExecutionContext* context)
    : FileSystemCallbacksBase(errorCallback, 0)
    , m_successCallback(successCallback)
    , m_executionContext(context)
{
}

void ResolveURICallbacks::didResolveURL(const String& name, const KURL& rootURL, FileSystemType type, const String& filePath, bool isDirectory)
{
    RefPtr<DOMFileSystem> filesystem = DOMFileSystem::create(m_executionContext.get(), name, type, rootURL);
    RefPtr<DirectoryEntry> root = filesystem->root();

    String absolutePath;
    if (!DOMFileSystemBase::pathToAbsolutePath(type, root.get(), filePath, absolutePath)) {
        m_errorCallback->handleEvent(FileError::create(FileError::INVALID_MODIFICATION_ERR).get());
        m_errorCallback.clear();
        return;
    }

    if (isDirectory)
        m_successCallback->handleEvent(DirectoryEntry::create(filesystem, absolutePath).get());
    else
        m_successCallback->handleEvent(FileEntry::create(filesystem, absolutePath).get());
    m_successCallback.clear();
}

// MetadataCallbacks ----------------------------------------------------------

PassOwnPtr<AsyncFileSystemCallbacks> MetadataCallbacks::create(PassRefPtr<MetadataCallback> successCallback, PassRefPtr<ErrorCallback> errorCallback, DOMFileSystemBase* fileSystem)
{
    return adoptPtr(static_cast<AsyncFileSystemCallbacks*>(new MetadataCallbacks(successCallback, errorCallback, fileSystem)));
}

MetadataCallbacks::MetadataCallbacks(PassRefPtr<MetadataCallback> successCallback, PassRefPtr<ErrorCallback> errorCallback, DOMFileSystemBase* fileSystem)
    : FileSystemCallbacksBase(errorCallback, fileSystem)
    , m_successCallback(successCallback)
{
}

void MetadataCallbacks::didReadMetadata(const FileMetadata& metadata)
{
    if (m_successCallback)
        m_successCallback->handleEvent(Metadata::create(metadata).get());
    m_successCallback.clear();
}

// FileWriterBaseCallbacks ----------------------------------------------------------

PassOwnPtr<AsyncFileSystemCallbacks> FileWriterBaseCallbacks::create(PassRefPtr<FileWriterBase> fileWriter, PassRefPtr<FileWriterBaseCallback> successCallback, PassRefPtr<ErrorCallback> errorCallback)
{
    return adoptPtr(static_cast<AsyncFileSystemCallbacks*>(new FileWriterBaseCallbacks(fileWriter, successCallback, errorCallback)));
}

FileWriterBaseCallbacks::FileWriterBaseCallbacks(PassRefPtr<FileWriterBase> fileWriter, PassRefPtr<FileWriterBaseCallback> successCallback, PassRefPtr<ErrorCallback> errorCallback)
    : FileSystemCallbacksBase(errorCallback, 0)
    , m_fileWriter(fileWriter)
    , m_successCallback(successCallback)
{
}

void FileWriterBaseCallbacks::didCreateFileWriter(PassOwnPtr<blink::WebFileWriter> fileWriter, long long length)
{
    m_fileWriter->initialize(fileWriter, length);
    if (m_successCallback)
        m_successCallback->handleEvent(m_fileWriter.release().get());
    m_successCallback.clear();
}

// VoidCallbacks --------------------------------------------------------------

PassOwnPtr<AsyncFileSystemCallbacks> VoidCallbacks::create(PassRefPtr<VoidCallback> successCallback, PassRefPtr<ErrorCallback> errorCallback, DOMFileSystemBase* fileSystem)
{
    return adoptPtr(static_cast<AsyncFileSystemCallbacks*>(new VoidCallbacks(successCallback, errorCallback, fileSystem)));
}

VoidCallbacks::VoidCallbacks(PassRefPtr<VoidCallback> successCallback, PassRefPtr<ErrorCallback> errorCallback, DOMFileSystemBase* fileSystem)
    : FileSystemCallbacksBase(errorCallback, fileSystem)
    , m_successCallback(successCallback)
{
}

void VoidCallbacks::didSucceed()
{
    if (m_successCallback)
        m_successCallback->handleEvent();
    m_successCallback.clear();
}

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

#include "config.h"
#include "modules/filesystem/FileSystemCallbacks.h"

#include "core/dom/ExecutionContext.h"
#include "core/fileapi/FileError.h"
#include "core/html/VoidCallback.h"
#include "modules/filesystem/DOMFilePath.h"
#include "modules/filesystem/DOMFileSystemBase.h"
#include "modules/filesystem/DirectoryEntry.h"
#include "modules/filesystem/DirectoryReader.h"
#include "modules/filesystem/Entry.h"
#include "modules/filesystem/EntryCallback.h"
#include "modules/filesystem/ErrorCallback.h"
#include "modules/filesystem/FileEntry.h"
#include "modules/filesystem/FileSystemCallback.h"
#include "modules/filesystem/FileWriterBase.h"
#include "modules/filesystem/FileWriterBaseCallback.h"
#include "modules/filesystem/Metadata.h"
#include "modules/filesystem/MetadataCallback.h"
#include "platform/FileMetadata.h"
#include "public/platform/WebFileWriter.h"

namespace WebCore {

FileSystemCallbacksBase::FileSystemCallbacksBase(PassRefPtr<ErrorCallback> errorCallback, DOMFileSystemBase* fileSystem)
    : m_errorCallback(errorCallback)
    , m_fileSystem(fileSystem)
{
    if (m_fileSystem)
        m_fileSystem->addPendingCallbacks();
}

FileSystemCallbacksBase::~FileSystemCallbacksBase()
{
    if (m_fileSystem)
        m_fileSystem->removePendingCallbacks();
}

void FileSystemCallbacksBase::didFail(int code)
{
    if (m_errorCallback) {
        m_errorCallback->handleEvent(FileError::create(static_cast<FileError::ErrorCode>(code)).get());
        m_errorCallback.clear();
    }
}

// EntryCallbacks -------------------------------------------------------------

PassOwnPtr<AsyncFileSystemCallbacks> EntryCallbacks::create(PassRefPtr<EntryCallback> successCallback, PassRefPtr<ErrorCallback> errorCallback, PassRefPtr<DOMFileSystemBase> fileSystem, const String& expectedPath, bool isDirectory)
{
    return adoptPtr(new EntryCallbacks(successCallback, errorCallback, fileSystem, expectedPath, isDirectory));
}

EntryCallbacks::EntryCallbacks(PassRefPtr<EntryCallback> successCallback, PassRefPtr<ErrorCallback> errorCallback, PassRefPtr<DOMFileSystemBase> fileSystem, const String& expectedPath, bool isDirectory)
    : FileSystemCallbacksBase(errorCallback, fileSystem.get())
    , m_successCallback(successCallback)
    , m_expectedPath(expectedPath)
    , m_isDirectory(isDirectory)
{
}

void EntryCallbacks::didSucceed()
{
    if (m_successCallback) {
        if (m_isDirectory)
            m_successCallback->handleEvent(DirectoryEntry::create(m_fileSystem, m_expectedPath).get());
        else
            m_successCallback->handleEvent(FileEntry::create(m_fileSystem, m_expectedPath).get());
    }
    m_successCallback.clear();
}

// EntriesCallbacks -----------------------------------------------------------

PassOwnPtr<AsyncFileSystemCallbacks> EntriesCallbacks::create(PassRefPtr<EntriesCallback> successCallback, PassRefPtr<ErrorCallback> errorCallback, PassRefPtr<DirectoryReaderBase> directoryReader, const String& basePath)
{
    return adoptPtr(new EntriesCallbacks(successCallback, errorCallback, directoryReader, basePath));
}

EntriesCallbacks::EntriesCallbacks(PassRefPtr<EntriesCallback> successCallback, PassRefPtr<ErrorCallback> errorCallback, PassRefPtr<DirectoryReaderBase> directoryReader, const String& basePath)
    : FileSystemCallbacksBase(errorCallback, directoryReader->filesystem())
    , m_successCallback(successCallback)
    , m_directoryReader(directoryReader)
    , m_basePath(basePath)
{
    ASSERT(m_directoryReader);
}

void EntriesCallbacks::didReadDirectoryEntry(const String& name, bool isDirectory)
{
    if (isDirectory)
        m_entries.append(DirectoryEntry::create(m_directoryReader->filesystem(), DOMFilePath::append(m_basePath, name)));
    else
        m_entries.append(FileEntry::create(m_directoryReader->filesystem(), DOMFilePath::append(m_basePath, name)));
}

void EntriesCallbacks::didReadDirectoryEntries(bool hasMore)
{
    m_directoryReader->setHasMoreEntries(hasMore);
    if (m_successCallback)
        m_successCallback->handleEvent(m_entries);
}

// FileSystemCallbacks --------------------------------------------------------

PassOwnPtr<AsyncFileSystemCallbacks> FileSystemCallbacks::create(PassRefPtr<FileSystemCallback> successCallback, PassRefPtr<ErrorCallback> errorCallback, ExecutionContext* executionContext, FileSystemType type)
{
    return adoptPtr(new FileSystemCallbacks(successCallback, errorCallback, executionContext, type));
}

FileSystemCallbacks::FileSystemCallbacks(PassRefPtr<FileSystemCallback> successCallback, PassRefPtr<ErrorCallback> errorCallback, ExecutionContext* context, FileSystemType type)
    : FileSystemCallbacksBase(errorCallback, 0)
    , m_successCallback(successCallback)
    , m_executionContext(context)
    , m_type(type)
{
}

void FileSystemCallbacks::didOpenFileSystem(const String& name, const KURL& rootURL)
{
    if (m_successCallback) {
        RefPtr<DOMFileSystem> fileSystem = DOMFileSystem::create(m_executionContext.get(), name, m_type, rootURL);
        m_successCallback->handleEvent(fileSystem.get());
        m_executionContext.clear();
    }
    m_successCallback.clear();
}

// ResolveURICallbacks --------------------------------------------------------

namespace {

class ErrorCallbackWrapper : public ErrorCallback {
public:
    static PassRefPtr<ErrorCallbackWrapper> create(PassRefPtr<EntryCallback> successCallback, PassRefPtr<ErrorCallback> errorCallback, PassRefPtr<DirectoryEntry> root, const String& filePath)
    {
        return adoptRef(new ErrorCallbackWrapper(successCallback, errorCallback, root, filePath));
    }

    virtual bool handleEvent(FileError* error)
    {
        ASSERT(error);
        if (error->code() == FileError::TYPE_MISMATCH_ERR)
            m_root->getFile(m_filePath, Dictionary(), m_successCallback, m_errorCallback);
        else if (m_errorCallback)
            m_errorCallback->handleEvent(error);
        return true;
    }

private:
    ErrorCallbackWrapper(PassRefPtr<EntryCallback> successCallback, PassRefPtr<ErrorCallback> errorCallback, PassRefPtr<DirectoryEntry> root, const String& filePath)
        : m_successCallback(successCallback)
        , m_errorCallback(errorCallback)
        , m_root(root)
        , m_filePath(filePath)
    {
        ASSERT(m_root);
    }

    RefPtr<EntryCallback> m_successCallback;
    RefPtr<ErrorCallback> m_errorCallback;
    RefPtr<DirectoryEntry> m_root;
    String m_filePath;
};

} // namespace

PassOwnPtr<AsyncFileSystemCallbacks> ResolveURICallbacks::create(PassRefPtr<EntryCallback> successCallback, PassRefPtr<ErrorCallback> errorCallback, ExecutionContext* executionContext)
{
    return adoptPtr(new ResolveURICallbacks(successCallback, errorCallback, executionContext));
}

ResolveURICallbacks::ResolveURICallbacks(PassRefPtr<EntryCallback> successCallback, PassRefPtr<ErrorCallback> errorCallback, ExecutionContext* context)
    : FileSystemCallbacksBase(errorCallback, 0)
    , m_successCallback(successCallback)
    , m_executionContext(context)
{
}

void ResolveURICallbacks::didResolveURL(const String& name, const KURL& rootURL, FileSystemType type, const String& filePath, bool isDirectory)
{
    RefPtr<DOMFileSystem> filesystem = DOMFileSystem::create(m_executionContext.get(), name, type, rootURL);
    RefPtr<DirectoryEntry> root = filesystem->root();

    String absolutePath;
    if (!DOMFileSystemBase::pathToAbsolutePath(type, root.get(), filePath, absolutePath)) {
        m_errorCallback->handleEvent(FileError::create(FileError::INVALID_MODIFICATION_ERR).get());
        m_errorCallback.clear();
        return;
    }

    if (isDirectory)
        m_successCallback->handleEvent(DirectoryEntry::create(filesystem, absolutePath).get());
    else
        m_successCallback->handleEvent(FileEntry::create(filesystem, absolutePath).get());
    m_successCallback.clear();
}

// MetadataCallbacks ----------------------------------------------------------

PassOwnPtr<AsyncFileSystemCallbacks> MetadataCallbacks::create(PassRefPtr<MetadataCallback> successCallback, PassRefPtr<ErrorCallback> errorCallback, DOMFileSystemBase* fileSystem)
{
    return adoptPtr(new MetadataCallbacks(successCallback, errorCallback, fileSystem));
}

MetadataCallbacks::MetadataCallbacks(PassRefPtr<MetadataCallback> successCallback, PassRefPtr<ErrorCallback> errorCallback, DOMFileSystemBase* fileSystem)
    : FileSystemCallbacksBase(errorCallback, fileSystem)
    , m_successCallback(successCallback)
{
}

void MetadataCallbacks::didReadMetadata(const FileMetadata& metadata)
{
    if (m_successCallback)
        m_successCallback->handleEvent(Metadata::create(metadata).get());
    m_successCallback.clear();
}

// FileWriterBaseCallbacks ----------------------------------------------------------

PassOwnPtr<AsyncFileSystemCallbacks> FileWriterBaseCallbacks::create(PassRefPtr<FileWriterBase> fileWriter, PassRefPtr<FileWriterBaseCallback> successCallback, PassRefPtr<ErrorCallback> errorCallback)
{
    return adoptPtr(new FileWriterBaseCallbacks(fileWriter, successCallback, errorCallback));
}

FileWriterBaseCallbacks::FileWriterBaseCallbacks(PassRefPtr<FileWriterBase> fileWriter, PassRefPtr<FileWriterBaseCallback> successCallback, PassRefPtr<ErrorCallback> errorCallback)
    : FileSystemCallbacksBase(errorCallback, 0)
    , m_fileWriter(fileWriter)
    , m_successCallback(successCallback)
{
}

void FileWriterBaseCallbacks::didCreateFileWriter(PassOwnPtr<blink::WebFileWriter> fileWriter, long long length)
{
    m_fileWriter->initialize(fileWriter, length);
    if (m_successCallback)
        m_successCallback->handleEvent(m_fileWriter.release().get());
    m_successCallback.clear();
}

// VoidCallbacks --------------------------------------------------------------

PassOwnPtr<AsyncFileSystemCallbacks> VoidCallbacks::create(PassRefPtr<VoidCallback> successCallback, PassRefPtr<ErrorCallback> errorCallback, DOMFileSystemBase* fileSystem)
{
    return adoptPtr(new VoidCallbacks(successCallback, errorCallback, fileSystem));
}

VoidCallbacks::VoidCallbacks(PassRefPtr<VoidCallback> successCallback, PassRefPtr<ErrorCallback> errorCallback, DOMFileSystemBase* fileSystem)
    : FileSystemCallbacksBase(errorCallback, fileSystem)
    , m_successCallback(successCallback)
{
}

void VoidCallbacks::didSucceed()
{
    if (m_successCallback)
        m_successCallback->handleEvent();
    m_successCallback.clear();
}

} // namespace
<|endoftext|>


<commit_before>/*
 * Executor.cpp
 *
 *  Created on: Oct 20, 2015
 *      Author: Péter Fankhauser
 *   Institute: ETH Zurich, Autonomous Systems Lab
 */

#include "free_gait_core/executor/Executor.hpp"
#include "free_gait_core/leg_motion/JointTrajectory.hpp"

namespace free_gait {

Executor::Executor(std::shared_ptr<StepCompleter> completer,
                   std::shared_ptr<StepComputer> computer,
                   std::shared_ptr<AdapterBase> adapter,
                   std::shared_ptr<State> state)
    : completer_(completer),
      computer_(computer),
      adapter_(adapter),
      state_(state),
      isInitialized_(false)
{
}

Executor::~Executor()
{
}

bool Executor::initialize()
{
  computer_->initialize();
  state_->initialize(adapter_->getLimbs(), adapter_->getBranches());
  reset();
  return isInitialized_ = true;
}

bool Executor::isInitialized() const
{
  return isInitialized_;
}

Executor::Mutex& Executor::getMutex()
{
  return mutex_;
}

bool Executor::advance(double dt)
{
  if (!isInitialized_) return false;
  updateStateWithMeasurements();
  bool executionStatus = adapter_->isExecutionOk();

  if (executionStatus) {
    if (!state_->getRobotExecutionStatus()) std::cout << "Continuing with free gait execution." << std::endl;
    state_->setRobotExecutionStatus(true);
  } else {
    if (state_->getRobotExecutionStatus()) std::cout << "Robot status is not OK, not continuing with free gait execution." << std::endl;
    state_->setRobotExecutionStatus(false);
    return true;
  }

  // Copying result from computer when done.
  if (!queue_.empty() && queue_.getCurrentStep().needsComputation() && computer_->isDone()) {
     computer_->getStep(queue_.getCurrentStep());
     computer_->resetIsDone();
  }

  // Advance queue.
  bool hasSwitchedStep;
  bool hasStartedStep;
  if (!queue_.advance(dt, hasSwitchedStep, hasStartedStep)) return false;

  // For a new switch in step, do some work on step for the transition.
  while (hasSwitchedStep) {
    auto& currentStep = queue_.getCurrentStep();
    if (!completer_->complete(*state_, queue_, currentStep)) {
      std::cerr << "Executor::advance: Could not complete step." << std::endl;
      return false;
    }
    if (currentStep.needsComputation() && !computer_->isBusy()) {
      computer_->setStep(currentStep);
      if (!computer_->compute()) {
        std::cerr << "Executor::advance: Could not compute step." << std::endl;
        return false;
      }
      if (computer_->isDone()) {
        computer_->getStep(queue_.getCurrentStep());
        computer_->resetIsDone();
      }
    }
    if (!queue_.advance(dt, hasSwitchedStep, hasStartedStep)) return false; // Advance again after completion.
  }

  if (hasStartedStep) {
    std::cout << "Switched step to:" << std::endl;
    std::cout << queue_.getCurrentStep() << std::endl;
  }

  if (!writeIgnoreContact()) return false;
  if (!writeIgnoreForPoseAdaptation()) return false;
  if (!writeSupportLegs()) return false;
  if (!writeSurfaceNormals()) return false;
  if (!writeLegMotion()) return false;
  if (!writeTorsoMotion()) return false;
  if (!adapter_->updateExtras(queue_, *state_)) return false;
//  std::cout << *state_ << std::endl;

  return true;
}

void Executor::reset()
{
  queue_.clear();
  initializeStateWithRobot();
  adapter_->updateExtras(queue_, *state_);
}

const StepQueue& Executor::getQueue() const
{
  return queue_;
}

StepQueue& Executor::getQueue()
{
  return queue_;
}

const State& Executor::getState() const
{
  return *state_;
}

const AdapterBase& Executor::getAdapter() const
{
  return *adapter_;
}

bool Executor::initializeStateWithRobot()
{
  for (const auto& limb : adapter_->getLimbs()) {
    state_->setSupportLeg(limb, adapter_->isLegGrounded(limb));
    state_->setIgnoreContact(limb, !adapter_->isLegGrounded(limb));
    state_->setIgnoreForPoseAdaptation(limb, !adapter_->isLegGrounded(limb));
    state_->removeSurfaceNormal(limb);
  }

  if (state_->getNumberOfSupportLegs() > 0) {
    state_->setControlSetup(BranchEnum::BASE, adapter_->getControlSetup(BranchEnum::BASE));
  } else {
    state_->setEmptyControlSetup(BranchEnum::BASE);
  }

  for (const auto& limb : adapter_->getLimbs()) {
    if (state_->isSupportLeg(limb)) {
      state_->setEmptyControlSetup(limb);
    } else {
      state_->setControlSetup(limb, adapter_->getControlSetup(limb));
    }
  }

  state_->setAllJointPositions(adapter_->getAllJointPositions());
  state_->setAllJointVelocities(adapter_->getAllJointVelocities());
//  state_->setAllJointAccelerations(adapter_->getAllJointAccelerations()); // TODO
  state_->setAllJointEfforts(adapter_->getAllJointEfforts());
  state_->setPositionWorldToBaseInWorldFrame(adapter_->getPositionWorldToBaseInWorldFrame());
  state_->setOrientationWorldToBase(adapter_->getOrientationWorldToBase());

  for (const auto& limb : adapter_->getLimbs()) {
    state_->setSupportLeg(limb, adapter_->isLegGrounded(limb));
    state_->setIgnoreContact(limb, !adapter_->isLegGrounded(limb));
  }

  state_->setRobotExecutionStatus(true);

  return true;
}

bool Executor::updateStateWithMeasurements()
{
  for (const auto& limb : adapter_->getLimbs()) {
    const auto& controlSetup = state_->getControlSetup(limb);
    if (!controlSetup.at(ControlLevel::Position)) {
      state_->setJointPositions(limb, adapter_->getJointPositions(limb));
    }
    if (!controlSetup.at(ControlLevel::Velocity)) {
      state_->setJointVelocities(limb, adapter_->getJointVelocities(limb));
    }
    if (!controlSetup.at(ControlLevel::Acceleration)) {
//      state_->setJointAccelerations(limb, adapter_->getJointAccelerations(limb));
    }
    if (!controlSetup.at(ControlLevel::Effort)) {
      state_->setJointEfforts(limb, adapter_->getJointEfforts(limb));
    }
  }

  const auto& controlSetup = state_->getControlSetup(BranchEnum::BASE);
  if (!controlSetup.at(ControlLevel::Position)) {
    state_->setPositionWorldToBaseInWorldFrame(adapter_->getPositionWorldToBaseInWorldFrame());
    state_->setOrientationWorldToBase(adapter_->getOrientationWorldToBase());
  }

  state_->setAllJointVelocities(adapter_->getAllJointVelocities());
  // TODO Copy also acceleraitons and torques.
//    state.setLinearVelocityBaseInWorldFrame(torso_->getMeasuredState().getLinearVelocityBaseInBaseFrame());
//    state.setAngularVelocityBaseInBaseFrame(torso_->getMeasuredState().getAngularVelocityBaseInBaseFrame());
  return true;
}

bool Executor::writeIgnoreContact()
{
  if (!queue_.active()) return true;
  const Step& step = queue_.getCurrentStep();
  for (const auto& limb : adapter_->getLimbs()) {
    if (step.hasLegMotion(limb)) {
      bool ignoreContact = step.getLegMotion(limb).isIgnoreContact();
      state_->setIgnoreContact(limb, ignoreContact);
    }
  }
  return true;
}

bool Executor::writeIgnoreForPoseAdaptation()
{
  if (!queue_.active()) return true;
  const Step& step = queue_.getCurrentStep();
  for (const auto& limb : adapter_->getLimbs()) {
    if (step.hasLegMotion(limb)) {
      bool ignoreForPoseAdaptation = step.getLegMotion(limb).isIgnoreForPoseAdaptation();
      state_->setIgnoreForPoseAdaptation(limb, ignoreForPoseAdaptation);
    }
  }
  return true;
}

bool Executor::writeSupportLegs()
{
  if (!queue_.active()) {
    for (const auto& limb : adapter_->getLimbs()) {
      state_->setSupportLeg(limb, !state_->isIgnoreContact(limb));
    }
    return true;
  }

  const Step& step = queue_.getCurrentStep();
  for (const auto& limb : adapter_->getLimbs()) {
    if (step.hasLegMotion(limb) || state_->isIgnoreContact(limb)) {
      state_->setSupportLeg(limb, false);
    } else {
      state_->setSupportLeg(limb, true);
    }
  }
  return true;
}

bool Executor::writeSurfaceNormals()
{
  if (!queue_.active()) return true;
  const Step& step = queue_.getCurrentStep();
  for (const auto& limb : adapter_->getLimbs()) {
    if (step.hasLegMotion(limb)) {
      if (step.getLegMotion(limb).hasSurfaceNormal()) {
        state_->setSurfaceNormal(limb, step.getLegMotion(limb).getSurfaceNormal());
      } else {
        state_->removeSurfaceNormal(limb);
      }
    }
  }
  return true;
}

bool Executor::writeLegMotion()
{
  for (const auto& limb : adapter_->getLimbs()) {
    if (state_->isSupportLeg(limb)) state_->setEmptyControlSetup(limb);
  }
  if (!queue_.active()) return true;

  const auto& step = queue_.getCurrentStep();
  if (!step.hasLegMotion()) return true;

  double time = queue_.getCurrentStep().getTime();
  for (const auto& limb : adapter_->getLimbs()) {
    if (!step.hasLegMotion(limb)) continue;
    auto const& legMotion = step.getLegMotion(limb);
    ControlSetup controlSetup = legMotion.getControlSetup();
    state_->setControlSetup(limb, controlSetup);

    switch (legMotion.getTrajectoryType()) {

      case LegMotionBase::TrajectoryType::EndEffector:
      {
        const auto& endEffectorMotion = dynamic_cast<const EndEffectorMotionBase&>(legMotion);
        if (controlSetup[ControlLevel::Position]) {
          const std::string& frameId = endEffectorMotion.getFrameId(ControlLevel::Position);
          if (!adapter_->frameIdExists(frameId)) {
            std::cerr << "Could not find frame '" << frameId << "' for free gait leg motion!" << std::endl;
            return false;
          }
          Position positionInBaseFrame = adapter_->transformPosition(frameId, "base", endEffectorMotion.evaluatePosition(time));
          JointPositions jointPositions;
          adapter_->getLimbJointPositionsFromPositionBaseToFootInBaseFrame(positionInBaseFrame, limb, jointPositions);
          state_->setJointPositions(limb, jointPositions);
        }
        break;
      }

      case LegMotionBase::TrajectoryType::Joints:
      {
        const auto& jointMotion = dynamic_cast<const JointMotionBase&>(legMotion);
        if (controlSetup[ControlLevel::Position]) state_->setJointPositions(limb, jointMotion.evaluatePosition(time));
//        if (controlSetup[ControlLevel::Velocity]) state_->setJointVelocities(limb, jointMotion.evaluateVelocity(time));
//        if (controlSetup[ControlLevel::Acceleration]) state_->setJointAcceleration(limb, jointMotion.evaluateAcceleration(time));
        if (controlSetup[ControlLevel::Effort]) state_->setJointEfforts(limb, jointMotion.evaluateEffort(time));
        break;
      }

      default:
        throw std::runtime_error("Executor::writeLegMotion() could not write leg motion of this type.");
        break;
    }
  }

  return true;
}

bool Executor::writeTorsoMotion()
{
  if (state_->getNumberOfSupportLegs() == 0) state_->setEmptyControlSetup(BranchEnum::BASE);
  if (!queue_.active()) return true;

  if (!queue_.getCurrentStep().hasBaseMotion()) return true;
  double time = queue_.getCurrentStep().getTime();
  // TODO Add frame handling.
  const auto& baseMotion = queue_.getCurrentStep().getBaseMotion();
  ControlSetup controlSetup = baseMotion.getControlSetup();
  state_->setControlSetup(BranchEnum::BASE, controlSetup);
  if (controlSetup[ControlLevel::Position]) {
    Pose pose = baseMotion.evaluatePose(time);
    state_->setPositionWorldToBaseInWorldFrame(pose.getPosition());
    state_->setOrientationWorldToBase(pose.getRotation());
  }
  if (controlSetup[ControlLevel::Velocity]) {
    Twist twist = baseMotion.evaluateTwist(time);
    state_->setLinearVelocityBaseInWorldFrame(twist.getTranslationalVelocity());
    state_->setAngularVelocityBaseInBaseFrame(twist.getRotationalVelocity());
  }
  // TODO Set more states.
  return true;
}

} /* namespace free_gait */
<commit_msg>deguggin outpu.<commit_after>/*
 * Executor.cpp
 *
 *  Created on: Oct 20, 2015
 *      Author: Péter Fankhauser
 *   Institute: ETH Zurich, Autonomous Systems Lab
 */

#include "free_gait_core/executor/Executor.hpp"
#include "free_gait_core/leg_motion/JointTrajectory.hpp"

namespace free_gait {

Executor::Executor(std::shared_ptr<StepCompleter> completer,
                   std::shared_ptr<StepComputer> computer,
                   std::shared_ptr<AdapterBase> adapter,
                   std::shared_ptr<State> state)
    : completer_(completer),
      computer_(computer),
      adapter_(adapter),
      state_(state),
      isInitialized_(false)
{
}

Executor::~Executor()
{
}

bool Executor::initialize()
{
  computer_->initialize();
  state_->initialize(adapter_->getLimbs(), adapter_->getBranches());
  reset();
  return isInitialized_ = true;
}

bool Executor::isInitialized() const
{
  return isInitialized_;
}

Executor::Mutex& Executor::getMutex()
{
  return mutex_;
}

bool Executor::advance(double dt)
{
  if (!isInitialized_) return false;
  updateStateWithMeasurements();
  bool executionStatus = adapter_->isExecutionOk();

  if (executionStatus) {
    if (!state_->getRobotExecutionStatus()) std::cout << "Continuing with free gait execution." << std::endl;
    state_->setRobotExecutionStatus(true);
  } else {
    if (state_->getRobotExecutionStatus()) std::cout << "Robot status is not OK, not continuing with free gait execution." << std::endl;
    state_->setRobotExecutionStatus(false);
    return true;
  }

  // Copying result from computer when done.
  if (!queue_.empty() && queue_.getCurrentStep().needsComputation() && computer_->isDone()) {
     computer_->getStep(queue_.getCurrentStep());
     computer_->resetIsDone();
  }

  // Advance queue.
  bool hasSwitchedStep;
  bool hasStartedStep;
  if (!queue_.advance(dt, hasSwitchedStep, hasStartedStep)) return false;

  // For a new switch in step, do some work on step for the transition.
  while (hasSwitchedStep) {
    auto& currentStep = queue_.getCurrentStep();
    if (!completer_->complete(*state_, queue_, currentStep)) {
      std::cerr << "Executor::advance: Could not complete step." << std::endl;
      return false;
    }
    if (currentStep.needsComputation() && !computer_->isBusy()) {
      computer_->setStep(currentStep);
      if (!computer_->compute()) {
        std::cerr << "Executor::advance: Could not compute step." << std::endl;
        return false;
      }
      if (computer_->isDone()) {
        computer_->getStep(queue_.getCurrentStep());
        computer_->resetIsDone();
      }
    }
    if (!queue_.advance(dt, hasSwitchedStep, hasStartedStep)) return false; // Advance again after completion.
  }

  if (hasStartedStep) {
    std::cout << "Switched step to:" << std::endl;
    std::cout << queue_.getCurrentStep() << std::endl;
  }

  if (!writeIgnoreContact()) return false;
  if (!writeIgnoreForPoseAdaptation()) return false;
  if (!writeSupportLegs()) return false;
  if (!writeSurfaceNormals()) return false;
  if (!writeLegMotion()) return false;
  if (!writeTorsoMotion()) return false;
  if (!adapter_->updateExtras(queue_, *state_)) return false;
//  std::cout << *state_ << std::endl;

  return true;
}

void Executor::reset()
{
  std::cout << "RESETTING EXECUUUUUUUUUUUUUUUUUUUUUUTOR" << std::endl;
  queue_.clear();
  initializeStateWithRobot();
  adapter_->updateExtras(queue_, *state_);
}

const StepQueue& Executor::getQueue() const
{
  return queue_;
}

StepQueue& Executor::getQueue()
{
  return queue_;
}

const State& Executor::getState() const
{
  return *state_;
}

const AdapterBase& Executor::getAdapter() const
{
  return *adapter_;
}

bool Executor::initializeStateWithRobot()
{
  for (const auto& limb : adapter_->getLimbs()) {
    state_->setSupportLeg(limb, adapter_->isLegGrounded(limb));
    state_->setIgnoreContact(limb, !adapter_->isLegGrounded(limb));
    state_->setIgnoreForPoseAdaptation(limb, !adapter_->isLegGrounded(limb));
    state_->removeSurfaceNormal(limb);
  }

  if (state_->getNumberOfSupportLegs() > 0) {
    state_->setControlSetup(BranchEnum::BASE, adapter_->getControlSetup(BranchEnum::BASE));
  } else {
    state_->setEmptyControlSetup(BranchEnum::BASE);
  }

  for (const auto& limb : adapter_->getLimbs()) {
    if (state_->isSupportLeg(limb)) {
      state_->setEmptyControlSetup(limb);
    } else {
      state_->setControlSetup(limb, adapter_->getControlSetup(limb));
    }
  }

  state_->setAllJointPositions(adapter_->getAllJointPositions());
  state_->setAllJointVelocities(adapter_->getAllJointVelocities());
//  state_->setAllJointAccelerations(adapter_->getAllJointAccelerations()); // TODO
  state_->setAllJointEfforts(adapter_->getAllJointEfforts());
  state_->setPositionWorldToBaseInWorldFrame(adapter_->getPositionWorldToBaseInWorldFrame());
  state_->setOrientationWorldToBase(adapter_->getOrientationWorldToBase());

  for (const auto& limb : adapter_->getLimbs()) {
    state_->setSupportLeg(limb, adapter_->isLegGrounded(limb));
    state_->setIgnoreContact(limb, !adapter_->isLegGrounded(limb));
  }

  state_->setRobotExecutionStatus(true);

  return true;
}

bool Executor::updateStateWithMeasurements()
{
  for (const auto& limb : adapter_->getLimbs()) {
    const auto& controlSetup = state_->getControlSetup(limb);
    if (!controlSetup.at(ControlLevel::Position)) {
      state_->setJointPositions(limb, adapter_->getJointPositions(limb));
    }
    if (!controlSetup.at(ControlLevel::Velocity)) {
      state_->setJointVelocities(limb, adapter_->getJointVelocities(limb));
    }
    if (!controlSetup.at(ControlLevel::Acceleration)) {
//      state_->setJointAccelerations(limb, adapter_->getJointAccelerations(limb));
    }
    if (!controlSetup.at(ControlLevel::Effort)) {
      state_->setJointEfforts(limb, adapter_->getJointEfforts(limb));
    }
  }

  const auto& controlSetup = state_->getControlSetup(BranchEnum::BASE);
  if (!controlSetup.at(ControlLevel::Position)) {
    state_->setPositionWorldToBaseInWorldFrame(adapter_->getPositionWorldToBaseInWorldFrame());
    state_->setOrientationWorldToBase(adapter_->getOrientationWorldToBase());
  }

  state_->setAllJointVelocities(adapter_->getAllJointVelocities());
  // TODO Copy also acceleraitons and torques.
//    state.setLinearVelocityBaseInWorldFrame(torso_->getMeasuredState().getLinearVelocityBaseInBaseFrame());
//    state.setAngularVelocityBaseInBaseFrame(torso_->getMeasuredState().getAngularVelocityBaseInBaseFrame());
  return true;
}

bool Executor::writeIgnoreContact()
{
  if (!queue_.active()) return true;
  const Step& step = queue_.getCurrentStep();
  for (const auto& limb : adapter_->getLimbs()) {
    if (step.hasLegMotion(limb)) {
      bool ignoreContact = step.getLegMotion(limb).isIgnoreContact();
      state_->setIgnoreContact(limb, ignoreContact);
    }
  }
  return true;
}

bool Executor::writeIgnoreForPoseAdaptation()
{
  if (!queue_.active()) return true;
  const Step& step = queue_.getCurrentStep();
  for (const auto& limb : adapter_->getLimbs()) {
    if (step.hasLegMotion(limb)) {
      bool ignoreForPoseAdaptation = step.getLegMotion(limb).isIgnoreForPoseAdaptation();
      state_->setIgnoreForPoseAdaptation(limb, ignoreForPoseAdaptation);
    }
  }
  return true;
}

bool Executor::writeSupportLegs()
{
  if (!queue_.active()) {
    for (const auto& limb : adapter_->getLimbs()) {
      state_->setSupportLeg(limb, !state_->isIgnoreContact(limb));
    }
    return true;
  }

  const Step& step = queue_.getCurrentStep();
  for (const auto& limb : adapter_->getLimbs()) {
    if (step.hasLegMotion(limb) || state_->isIgnoreContact(limb)) {
      state_->setSupportLeg(limb, false);
    } else {
      state_->setSupportLeg(limb, true);
    }
  }
  return true;
}

bool Executor::writeSurfaceNormals()
{
  if (!queue_.active()) return true;
  const Step& step = queue_.getCurrentStep();
  for (const auto& limb : adapter_->getLimbs()) {
    if (step.hasLegMotion(limb)) {
      if (step.getLegMotion(limb).hasSurfaceNormal()) {
        state_->setSurfaceNormal(limb, step.getLegMotion(limb).getSurfaceNormal());
      } else {
        state_->removeSurfaceNormal(limb);
      }
    }
  }
  return true;
}

bool Executor::writeLegMotion()
{
  for (const auto& limb : adapter_->getLimbs()) {
    if (state_->isSupportLeg(limb)) state_->setEmptyControlSetup(limb);
  }
  if (!queue_.active()) return true;

  const auto& step = queue_.getCurrentStep();
  if (!step.hasLegMotion()) return true;

  double time = queue_.getCurrentStep().getTime();
  for (const auto& limb : adapter_->getLimbs()) {
    if (!step.hasLegMotion(limb)) continue;
    auto const& legMotion = step.getLegMotion(limb);
    ControlSetup controlSetup = legMotion.getControlSetup();
    state_->setControlSetup(limb, controlSetup);

    switch (legMotion.getTrajectoryType()) {

      case LegMotionBase::TrajectoryType::EndEffector:
      {
        const auto& endEffectorMotion = dynamic_cast<const EndEffectorMotionBase&>(legMotion);
        if (controlSetup[ControlLevel::Position]) {
          const std::string& frameId = endEffectorMotion.getFrameId(ControlLevel::Position);
          if (!adapter_->frameIdExists(frameId)) {
            std::cerr << "Could not find frame '" << frameId << "' for free gait leg motion!" << std::endl;
            return false;
          }
          Position positionInBaseFrame = adapter_->transformPosition(frameId, "base", endEffectorMotion.evaluatePosition(time));
          JointPositions jointPositions;
          adapter_->getLimbJointPositionsFromPositionBaseToFootInBaseFrame(positionInBaseFrame, limb, jointPositions);
          state_->setJointPositions(limb, jointPositions);
        }
        break;
      }

      case LegMotionBase::TrajectoryType::Joints:
      {
        const auto& jointMotion = dynamic_cast<const JointMotionBase&>(legMotion);
        if (controlSetup[ControlLevel::Position]) state_->setJointPositions(limb, jointMotion.evaluatePosition(time));
//        if (controlSetup[ControlLevel::Velocity]) state_->setJointVelocities(limb, jointMotion.evaluateVelocity(time));
//        if (controlSetup[ControlLevel::Acceleration]) state_->setJointAcceleration(limb, jointMotion.evaluateAcceleration(time));
        if (controlSetup[ControlLevel::Effort]) state_->setJointEfforts(limb, jointMotion.evaluateEffort(time));
        break;
      }

      default:
        throw std::runtime_error("Executor::writeLegMotion() could not write leg motion of this type.");
        break;
    }
  }

  return true;
}

bool Executor::writeTorsoMotion()
{
  if (state_->getNumberOfSupportLegs() == 0) state_->setEmptyControlSetup(BranchEnum::BASE);
  if (!queue_.active()) return true;

  if (!queue_.getCurrentStep().hasBaseMotion()) return true;
  double time = queue_.getCurrentStep().getTime();
  // TODO Add frame handling.
  const auto& baseMotion = queue_.getCurrentStep().getBaseMotion();
  ControlSetup controlSetup = baseMotion.getControlSetup();
  state_->setControlSetup(BranchEnum::BASE, controlSetup);
  if (controlSetup[ControlLevel::Position]) {
    Pose pose = baseMotion.evaluatePose(time);
    state_->setPositionWorldToBaseInWorldFrame(pose.getPosition());
    state_->setOrientationWorldToBase(pose.getRotation());
  }
  if (controlSetup[ControlLevel::Velocity]) {
    Twist twist = baseMotion.evaluateTwist(time);
    state_->setLinearVelocityBaseInWorldFrame(twist.getTranslationalVelocity());
    state_->setAngularVelocityBaseInBaseFrame(twist.getRotationalVelocity());
  }
  // TODO Set more states.
  return true;
}

} /* namespace free_gait */
<|endoftext|>


<commit_before>/** \brief  Generates a list of religious studies essay collections.
 *  \author Dr. Johannes Ruscheinski (johannes.ruscheinski@uni-tuebingen.de)
 *
 *  \copyright 2019 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 <string>
#include <unordered_map>
#include "FileUtil.h"
#include "MARC.h"

namespace {

void CollectArticleCollectionPPNs(MARC::Reader * const reader,
                                  std::unordered_map<std::string, unsigned> * const article_collection_ppns_and_counts)
{
    article_collection_ppns_and_counts->clear();
    while (const MARC::Record record = reader->read()) {
        if (record.isArticle()) {
            const std::string parent_ppn(MARC::GetParentPPN(record));
            if (parent_ppn.empty())
                continue;

            auto article_collection_ppn_and_count(article_collection_ppns_and_counts->find(parent_ppn));
            if (article_collection_ppn_and_count == article_collection_ppns_and_counts->end())
                article_collection_ppns_and_counts->insert({ parent_ppn, 1 });
            else
                ++(article_collection_ppn_and_count->second);
        }
    }
}

// If any of the following matches, we have an essay collection:
struct EssayCollectionMatch {
    MARC::Tag tag_;
    char subfield_code_;
    std::string subfield_contents_;
public:
    EssayCollectionMatch(const MARC::Tag &tag, const char subfield_code, const std::string &subfield_contents)
        : tag_(tag), subfield_code_(subfield_code), subfield_contents_(subfield_contents) { }
};

const std::vector<EssayCollectionMatch> essay_collection_matches{
    { "935", 'c', "fe"               },
    { "655", 'a', "Aufsatzsammlung"  },
    { "689", 'a', "Aufsatzsammlung"  },
    { "655", 'a', "Festschrift"      },
    { "655", 'a', "Konferenzschrift" },
    { "689", 'a', "Konferenzschrift" },
    { "689", 'a', "Kongress"         },
    { "935", 'c', "gkko"             },
};

inline bool ConsistsOfDigitsOnly(const std::string &s) {
    for (const char ch : s) {
        if (not StringUtil::IsDigit(ch))
            return false;
    }

    return true;
}

std::string GetPublicationYear(const MARC::Record &record) {
    const auto _008_field(record.findTag("008"));
    if (likely(_008_field != record.end())) {
        const auto &field_contents(_008_field->getContents());
        if (likely(field_contents.length() >= 12)) {
            const std::string year_candidate(field_contents.substr(7, 4));
            if (ConsistsOfDigitsOnly(year_candidate) and year_candidate != "9999")
                return year_candidate;
        }
    }

    const auto _190_field(record.findTag("190"));
    if (_190_field != record.end()) {
        const std::string year_candidate(_190_field->getFirstSubfieldWithCode('j'));
        if (not year_candidate.empty())
            return year_candidate;
    }

    return "????";
}

std::string ShortenTitle(const std::string &full_title, const size_t max_length) {
    const auto full_title_length(full_title.length());
    if (full_title_length <= max_length)
        return full_title;

    return full_title.substr(0, max_length - 3) + "...";
}

bool IsTOC(const MARC::Record &record) {
    for (const auto &_856_field : record.getTagRange("856")) {
        for (const auto subfield : _856_field.getSubfields()) {
            if (subfield.code_ == '3' and ::strcasecmp(subfield.value_.c_str(), "Inhaltsverzeichnis") == 0)
                return true;
        }
    }

    return false;
}

bool IsMonographOfInterest(const MARC::Record &record) {
    if (not record.isMonograph())
        return false;

    for (const auto &_655_field : record.getTagRange("655")) {
        const auto a_subfield(_655_field.getFirstSubfieldWithCode('a'));
        if (::strcasecmp(a_subfield.c_str(), "Aufsatzsammlung") == 0
            or ::strcasecmp(a_subfield.c_str(), "Festschrift") == 0
            or ::strcasecmp(a_subfield.c_str(), "Konferenzschrift") == 0)
            return true;
    }

    return false;
}

void MarkArticleCollections(MARC::Reader * const reader, File * const output,
                            const std::unordered_map<std::string, unsigned> &article_collection_ppns_and_counts)
{
    unsigned count(0);
    while (MARC::Record record = reader->read()) {
        if (IsMonographOfInterest(record)) {
            const auto collection_ppn_and_article_count(article_collection_ppns_and_counts.find(record.getControlNumber()));
            if (collection_ppn_and_article_count != article_collection_ppns_and_counts.cend()) {
                ++count;

                const auto ssgns(record.getSSGNs());
                if (ssgns.find("0") != ssgns.cend()) {
                    const auto publication_year(GetPublicationYear(record));
                    *output << record.getControlNumber() << ", " << ShortenTitle(record.getMainTitle(), 60) << ", "
                            << (IsTOC(record) ? "Ja" : "Nein") << ", " << publication_year << ", "
                            << collection_ppn_and_article_count->second << '\n';
                }
            }
        }
    }

    LOG_INFO("Found " + std::to_string(count) + " article collections.");
}

} // unnamed namespace

int Main(int argc, char **argv) {
    if (argc != 3)
        ::Usage("marc_input list");

    std::unique_ptr<MARC::Reader> marc_reader(MARC::Reader::Factory(argv[1]));
    const auto output(FileUtil::OpenOutputFileOrDie(argv[2]));

    std::unordered_map<std::string, unsigned> article_collection_ppns_and_counts;
    CollectArticleCollectionPPNs(marc_reader.get(), &article_collection_ppns_and_counts);
    marc_reader->rewind();
    MarkArticleCollections(marc_reader.get(), output.get(), article_collection_ppns_and_counts);

    return EXIT_SUCCESS;
}
<commit_msg>Cleanup.<commit_after>/** \brief  Generates a list of religious studies essay collections.
 *  \author Dr. Johannes Ruscheinski (johannes.ruscheinski@uni-tuebingen.de)
 *
 *  \copyright 2019 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 <string>
#include <unordered_map>
#include "FileUtil.h"
#include "MARC.h"

namespace {

void CollectArticleCollectionPPNs(MARC::Reader * const reader,
                                  std::unordered_map<std::string, unsigned> * const article_collection_ppns_and_counts)
{
    article_collection_ppns_and_counts->clear();
    while (const MARC::Record record = reader->read()) {
        if (record.isArticle()) {
            const std::string parent_ppn(MARC::GetParentPPN(record));
            if (parent_ppn.empty())
                continue;

            auto article_collection_ppn_and_count(article_collection_ppns_and_counts->find(parent_ppn));
            if (article_collection_ppn_and_count == article_collection_ppns_and_counts->end())
                article_collection_ppns_and_counts->insert({ parent_ppn, 1 });
            else
                ++(article_collection_ppn_and_count->second);
        }
    }
}

// If any of the following matches, we have an essay collection:
struct EssayCollectionMatch {
    MARC::Tag tag_;
    char subfield_code_;
    std::string subfield_contents_;
public:
    EssayCollectionMatch(const MARC::Tag &tag, const char subfield_code, const std::string &subfield_contents)
        : tag_(tag), subfield_code_(subfield_code), subfield_contents_(subfield_contents) { }
};

inline bool ConsistsOfDigitsOnly(const std::string &s) {
    for (const char ch : s) {
        if (not StringUtil::IsDigit(ch))
            return false;
    }

    return true;
}

std::string GetPublicationYear(const MARC::Record &record) {
    const auto _008_field(record.findTag("008"));
    if (likely(_008_field != record.end())) {
        const auto &field_contents(_008_field->getContents());
        if (likely(field_contents.length() >= 12)) {
            const std::string year_candidate(field_contents.substr(7, 4));
            if (ConsistsOfDigitsOnly(year_candidate) and year_candidate != "9999")
                return year_candidate;
        }
    }

    const auto _190_field(record.findTag("190"));
    if (_190_field != record.end()) {
        const std::string year_candidate(_190_field->getFirstSubfieldWithCode('j'));
        if (not year_candidate.empty())
            return year_candidate;
    }

    return "????";
}

std::string ShortenTitle(const std::string &full_title, const size_t max_length) {
    const auto full_title_length(full_title.length());
    if (full_title_length <= max_length)
        return full_title;

    return full_title.substr(0, max_length - 3) + "...";
}

bool HasTOC(const MARC::Record &record) {
    for (const auto &_856_field : record.getTagRange("856")) {
        for (const auto subfield : _856_field.getSubfields()) {
            if (subfield.code_ == '3' and ::strcasecmp(subfield.value_.c_str(), "Inhaltsverzeichnis") == 0)
                return true;
        }
    }

    return false;
}

bool IsMonographOfInterest(const MARC::Record &record) {
    if (not record.isMonograph())
        return false;

    for (const auto &_655_field : record.getTagRange("655")) {
        const auto a_subfield(_655_field.getFirstSubfieldWithCode('a'));
        if (::strcasecmp(a_subfield.c_str(), "Aufsatzsammlung") == 0
            or ::strcasecmp(a_subfield.c_str(), "Festschrift") == 0
            or ::strcasecmp(a_subfield.c_str(), "Konferenzschrift") == 0)
            return true;
    }

    return false;
}

void MarkArticleCollections(MARC::Reader * const reader, File * const output,
                            const std::unordered_map<std::string, unsigned> &article_collection_ppns_and_counts)
{
    unsigned count(0);
    while (MARC::Record record = reader->read()) {
        if (IsMonographOfInterest(record)) {
            const auto collection_ppn_and_article_count(article_collection_ppns_and_counts.find(record.getControlNumber()));
            if (collection_ppn_and_article_count != article_collection_ppns_and_counts.cend()) {
                ++count;

                const auto ssgns(record.getSSGNs());
                if (ssgns.find("0") != ssgns.cend()) {
                    const auto publication_year(GetPublicationYear(record));
                    *output << record.getControlNumber() << ", " << ShortenTitle(record.getMainTitle(), 60) << ", "
                            << (HasTOC(record) ? "Ja" : "Nein") << ", " << publication_year << ", "
                            << collection_ppn_and_article_count->second << '\n';
                }
            }
        }
    }

    LOG_INFO("Found " + std::to_string(count) + " article collections.");
}

} // unnamed namespace

int Main(int argc, char **argv) {
    if (argc != 3)
        ::Usage("marc_input list");

    std::unique_ptr<MARC::Reader> marc_reader(MARC::Reader::Factory(argv[1]));
    const auto output(FileUtil::OpenOutputFileOrDie(argv[2]));

    std::unordered_map<std::string, unsigned> article_collection_ppns_and_counts;
    CollectArticleCollectionPPNs(marc_reader.get(), &article_collection_ppns_and_counts);
    marc_reader->rewind();
    MarkArticleCollections(marc_reader.get(), output.get(), article_collection_ppns_and_counts);

    return EXIT_SUCCESS;
}
<|endoftext|>


<commit_before>/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.
 */

/*
 * Copyright (C) 2015 ScyllaDB
 *
 * Modified by 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 "create_index_statement.hh"
#include "prepared_statement.hh"
#include "validation.hh"
#include "service/storage_proxy.hh"
#include "service/migration_manager.hh"
#include "service/storage_service.hh"
#include "schema.hh"
#include "schema_builder.hh"
#include "request_validations.hh"
#include "database.hh"
#include "index/target_parser.hh"

#include <boost/range/adaptor/transformed.hpp>
#include <boost/algorithm/string/join.hpp>

namespace cql3 {

namespace statements {

create_index_statement::create_index_statement(::shared_ptr<cf_name> name,
                                               ::shared_ptr<index_name> index_name,
                                               std::vector<::shared_ptr<index_target::raw>> raw_targets,
                                               ::shared_ptr<index_prop_defs> properties,
                                               bool if_not_exists)
    : schema_altering_statement(name)
    , _index_name(index_name->get_idx())
    , _raw_targets(raw_targets)
    , _properties(properties)
    , _if_not_exists(if_not_exists)
{
}

future<>
create_index_statement::check_access(const service::client_state& state) {
    return state.has_column_family_access(keyspace(), column_family(), auth::permission::ALTER);
}

void
create_index_statement::validate(service::storage_proxy& proxy, const service::client_state& state)
{
    auto& db = proxy.get_db().local();
    auto schema = validation::validate_column_family(db, keyspace(), column_family());

    if (schema->is_counter()) {
        throw exceptions::invalid_request_exception("Secondary indexes are not supported on counter tables");
    }

    if (schema->is_view()) {
        throw exceptions::invalid_request_exception("Secondary indexes are not supported on materialized views");
    }

    if (schema->is_dense()) {
        throw exceptions::invalid_request_exception(
                "Secondary indexes are not supported on COMPACT STORAGE tables that have clustering columns");
    }

    validate_for_local_index(schema);

    std::vector<::shared_ptr<index_target>> targets;
    for (auto& raw_target : _raw_targets) {
        targets.emplace_back(raw_target->prepare(schema));
    }

    if (targets.empty() && !_properties->is_custom) {
        throw exceptions::invalid_request_exception("Only CUSTOM indexes can be created without specifying a target column");
    }

    if (targets.size() > 1) {
        validate_targets_for_multi_column_index(targets);
    }

    for (auto& target : targets) {
        auto* ident = std::get_if<::shared_ptr<column_identifier>>(&target->value);
        if (!ident) {
            continue;
        }
        auto cd = schema->get_column_definition((*ident)->name());

        if (cd == nullptr) {
            throw exceptions::invalid_request_exception(
                    format("No column definition found for column {}", target->as_string()));
        }

        //NOTICE(sarna): Should be lifted after resolving issue #2963
        if (cd->is_static()) {
            throw exceptions::invalid_request_exception("Indexing static columns is not implemented yet.");
        }

        if (cd->type->references_duration()) {
            using request_validations::check_false;
            const auto& ty = *cd->type;

            check_false(ty.is_collection(), "Secondary indexes are not supported on collections containing durations");
            check_false(ty.is_tuple(), "Secondary indexes are not supported on tuples containing durations");
            check_false(ty.is_user_type(), "Secondary indexes are not supported on UDTs containing durations");

            // We're a duration.
            throw exceptions::invalid_request_exception("Secondary indexes are not supported on duration columns");
        }

        // Origin TODO: we could lift that limitation
        if ((schema->is_dense() || !schema->thrift().has_compound_comparator()) && cd->is_primary_key()) {
            throw exceptions::invalid_request_exception(
                    "Secondary indexes are not supported on PRIMARY KEY columns in COMPACT STORAGE tables");
        }

        if (cd->kind == column_kind::partition_key && cd->is_on_all_components()) {
            throw exceptions::invalid_request_exception(
                    format("Cannot create secondary index on partition key column {}",
                            target->as_string()));
        }

        bool is_map = dynamic_cast<const collection_type_impl *>(cd->type.get()) != nullptr
                      && dynamic_cast<const collection_type_impl *>(cd->type.get())->is_map();
        bool is_collection = cd->type->is_collection();
        bool is_frozen_collection = is_collection && !cd->type->is_multi_cell();

        if (is_frozen_collection) {
            validate_for_frozen_collection(target);
        } else if (is_collection) {
            // NOTICE(sarna): should be lifted after #2962 (indexes on non-frozen collections) is implemented
            throw exceptions::invalid_request_exception(
                    format("Cannot create secondary index on non-frozen collection column {}", cd->name_as_text()));
        } else {
            validate_not_full_index(target);
            validate_is_values_index_if_target_column_not_collection(cd, target);
            validate_target_column_is_map_if_index_involves_keys(is_map, target);
        }
    }

    if (db.existing_index_names(keyspace()).count(_index_name) > 0) {
        if (_if_not_exists) {
            return;
        } else {
            throw exceptions::invalid_request_exception("Index already exists");
        }
    }

    _properties->validate();
}

void create_index_statement::validate_for_local_index(schema_ptr schema) const {
    if (!_raw_targets.empty()) {
            if (const auto* index_pk = std::get_if<std::vector<::shared_ptr<column_identifier::raw>>>(&_raw_targets.front()->value)) {
                auto base_pk_identifiers = *index_pk | boost::adaptors::transformed([&schema] (const ::shared_ptr<column_identifier::raw>& raw_ident) {
                    return raw_ident->prepare_column_identifier(schema);
                });
                auto remaining_base_pk_columns = schema->partition_key_columns();
                auto next_expected_base_column = remaining_base_pk_columns.begin();
                for (const auto& ident : base_pk_identifiers) {
                    auto it = schema->columns_by_name().find(ident->name());
                    if (it == schema->columns_by_name().end() || !it->second->is_partition_key()) {
                        throw exceptions::invalid_request_exception(format("Local index definition must contain full partition key only. Redundant column: {}", ident->to_string()));
                    }
                    if (next_expected_base_column == remaining_base_pk_columns.end()) {
                        throw exceptions::invalid_request_exception(format("Duplicate column definition in local index: {}", it->first));
                    }
                    if (&*next_expected_base_column != it->second) {
                        break;
                    }
                    ++next_expected_base_column;
                }
                if (next_expected_base_column != remaining_base_pk_columns.end()) {
                    throw exceptions::invalid_request_exception(format("Local index definition must contain full partition key only. Missing column: {}", next_expected_base_column->name_as_text()));
                }
                if (_raw_targets.size() == 1) {
                    throw exceptions::invalid_request_exception(format("Local index definition must provide an indexed column, not just partition key"));
                }
            }
        }
        for (unsigned i = 1; i < _raw_targets.size(); ++i) {
            if (std::holds_alternative<index_target::raw::multiple_columns>(_raw_targets[i]->value)) {
                throw exceptions::invalid_request_exception(format("Multi-column index targets are currently only supported for partition key"));
            }
        }
}

void create_index_statement::validate_for_frozen_collection(::shared_ptr<index_target> target) const
{
    if (target->type != index_target::target_type::full) {
        throw exceptions::invalid_request_exception(
                format("Cannot create index on {} of frozen<map> column {}",
                        index_target::index_option(target->type),
                        target->as_string()));
    }
}

void create_index_statement::validate_not_full_index(::shared_ptr<index_target> target) const
{
    if (target->type == index_target::target_type::full) {
        throw exceptions::invalid_request_exception("full() indexes can only be created on frozen collections");
    }
}

void create_index_statement::validate_is_values_index_if_target_column_not_collection(
        const column_definition* cd, ::shared_ptr<index_target> target) const
{
    if (!cd->type->is_collection()
            && target->type != index_target::target_type::values) {
        throw exceptions::invalid_request_exception(
                format("Cannot create index on {} of column {}; only non-frozen collections support {} indexes",
                       index_target::index_option(target->type),
                       target->as_string(),
                       index_target::index_option(target->type)));
    }
}

void create_index_statement::validate_target_column_is_map_if_index_involves_keys(bool is_map, ::shared_ptr<index_target> target) const
{
    if (target->type == index_target::target_type::keys
            || target->type == index_target::target_type::keys_and_values) {
        if (!is_map) {
            throw exceptions::invalid_request_exception(
                    format("Cannot create index on {} of column {} with non-map type",
                           index_target::index_option(target->type), target->as_string()));
        }
    }
}

void create_index_statement::validate_targets_for_multi_column_index(std::vector<::shared_ptr<index_target>> targets) const
{
    if (!_properties->is_custom) {
        if (targets.size() > 2 || (targets.size() == 2 && std::holds_alternative<index_target::single_column>(targets.front()->value))) {
            throw exceptions::invalid_request_exception("Only CUSTOM indexes support multiple columns");
        }
    }
    std::unordered_set<sstring> columns;
    for (auto& target : targets) {
        if (columns.count(target->as_string()) > 0) {
            throw exceptions::invalid_request_exception(format("Duplicate column {} in index target list", target->as_string()));
        }
        columns.emplace(target->as_string());
    }
}

future<::shared_ptr<cql_transport::event::schema_change>>
create_index_statement::announce_migration(service::storage_proxy& proxy, bool is_local_only) {
    if (!service::get_local_storage_service().cluster_supports_indexes()) {
        throw exceptions::invalid_request_exception("Index support is not enabled");
    }
    auto& db = proxy.get_db().local();
    auto schema = db.find_schema(keyspace(), column_family());
    std::vector<::shared_ptr<index_target>> targets;
    for (auto& raw_target : _raw_targets) {
        targets.emplace_back(raw_target->prepare(schema));
    }
    sstring accepted_name = _index_name;
    if (accepted_name.empty()) {
        std::optional<sstring> index_name_root;
        if (targets.size() == 1) {
           index_name_root = targets[0]->as_string();
        } else if ((targets.size() == 2 && std::holds_alternative<index_target::multiple_columns>(targets.front()->value))) {
            index_name_root = targets[1]->as_string();
        }
        accepted_name = db.get_available_index_name(keyspace(), column_family(), index_name_root);
    }
    index_metadata_kind kind;
    index_options_map index_options;
    if (_properties->is_custom) {
        kind = index_metadata_kind::custom;
        index_options = _properties->get_options();
    } else {
        kind = schema->is_compound() ? index_metadata_kind::composites : index_metadata_kind::keys;
    }
    auto index = make_index_metadata(schema, targets, accepted_name, kind, index_options);
    auto existing_index = schema->find_index_noname(index);
    if (existing_index) {
        if (_if_not_exists) {
            return make_ready_future<::shared_ptr<cql_transport::event::schema_change>>(nullptr);
        } else {
            throw exceptions::invalid_request_exception(
                    format("Index {} is a duplicate of existing index {}", index.name(), existing_index.value().name()));
        }
    }
    ++_cql_stats->secondary_index_creates;
    schema_builder builder{schema};
    builder.with_index(index);
    return service::get_local_migration_manager().announce_column_family_update(
            builder.build(), false, {}, is_local_only).then([this]() {
        using namespace cql_transport;
        return make_shared<event::schema_change>(
                event::schema_change::change_type::UPDATED,
                event::schema_change::target_type::TABLE,
                keyspace(),
                column_family());
    });
}

std::unique_ptr<cql3::statements::prepared_statement>
create_index_statement::prepare(database& db, cql_stats& stats) {
    _cql_stats = &stats;
    return std::make_unique<prepared_statement>(make_shared<create_index_statement>(*this));
}

index_metadata create_index_statement::make_index_metadata(schema_ptr schema,
                                                           const std::vector<::shared_ptr<index_target>>& targets,
                                                           const sstring& name,
                                                           index_metadata_kind kind,
                                                           const index_options_map& options)
{
    index_options_map new_options = options;
    auto target_option = secondary_index::target_parser::serialize_targets(targets);
    new_options.emplace(index_target::target_option_name, target_option);

    const auto& first_target = targets.front()->value;
    return index_metadata{name, new_options, kind, index_metadata::is_local_index(std::holds_alternative<index_target::multiple_columns>(first_target))};
}

}

}
<commit_msg>cql3: disallow non-frozen UDTs when creating secondary indexes<commit_after>/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.
 */

/*
 * Copyright (C) 2015 ScyllaDB
 *
 * Modified by 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 "create_index_statement.hh"
#include "prepared_statement.hh"
#include "validation.hh"
#include "service/storage_proxy.hh"
#include "service/migration_manager.hh"
#include "service/storage_service.hh"
#include "schema.hh"
#include "schema_builder.hh"
#include "request_validations.hh"
#include "database.hh"
#include "index/target_parser.hh"

#include <boost/range/adaptor/transformed.hpp>
#include <boost/algorithm/string/join.hpp>

namespace cql3 {

namespace statements {

create_index_statement::create_index_statement(::shared_ptr<cf_name> name,
                                               ::shared_ptr<index_name> index_name,
                                               std::vector<::shared_ptr<index_target::raw>> raw_targets,
                                               ::shared_ptr<index_prop_defs> properties,
                                               bool if_not_exists)
    : schema_altering_statement(name)
    , _index_name(index_name->get_idx())
    , _raw_targets(raw_targets)
    , _properties(properties)
    , _if_not_exists(if_not_exists)
{
}

future<>
create_index_statement::check_access(const service::client_state& state) {
    return state.has_column_family_access(keyspace(), column_family(), auth::permission::ALTER);
}

void
create_index_statement::validate(service::storage_proxy& proxy, const service::client_state& state)
{
    auto& db = proxy.get_db().local();
    auto schema = validation::validate_column_family(db, keyspace(), column_family());

    if (schema->is_counter()) {
        throw exceptions::invalid_request_exception("Secondary indexes are not supported on counter tables");
    }

    if (schema->is_view()) {
        throw exceptions::invalid_request_exception("Secondary indexes are not supported on materialized views");
    }

    if (schema->is_dense()) {
        throw exceptions::invalid_request_exception(
                "Secondary indexes are not supported on COMPACT STORAGE tables that have clustering columns");
    }

    validate_for_local_index(schema);

    std::vector<::shared_ptr<index_target>> targets;
    for (auto& raw_target : _raw_targets) {
        targets.emplace_back(raw_target->prepare(schema));
    }

    if (targets.empty() && !_properties->is_custom) {
        throw exceptions::invalid_request_exception("Only CUSTOM indexes can be created without specifying a target column");
    }

    if (targets.size() > 1) {
        validate_targets_for_multi_column_index(targets);
    }

    for (auto& target : targets) {
        auto* ident = std::get_if<::shared_ptr<column_identifier>>(&target->value);
        if (!ident) {
            continue;
        }
        auto cd = schema->get_column_definition((*ident)->name());

        if (cd == nullptr) {
            throw exceptions::invalid_request_exception(
                    format("No column definition found for column {}", target->as_string()));
        }

        //NOTICE(sarna): Should be lifted after resolving issue #2963
        if (cd->is_static()) {
            throw exceptions::invalid_request_exception("Indexing static columns is not implemented yet.");
        }

        if (cd->type->references_duration()) {
            using request_validations::check_false;
            const auto& ty = *cd->type;

            check_false(ty.is_collection(), "Secondary indexes are not supported on collections containing durations");
            check_false(ty.is_tuple(), "Secondary indexes are not supported on tuples containing durations");
            check_false(ty.is_user_type(), "Secondary indexes are not supported on UDTs containing durations");

            // We're a duration.
            throw exceptions::invalid_request_exception("Secondary indexes are not supported on duration columns");
        }

        // Origin TODO: we could lift that limitation
        if ((schema->is_dense() || !schema->thrift().has_compound_comparator()) && cd->is_primary_key()) {
            throw exceptions::invalid_request_exception(
                    "Secondary indexes are not supported on PRIMARY KEY columns in COMPACT STORAGE tables");
        }

        if (cd->kind == column_kind::partition_key && cd->is_on_all_components()) {
            throw exceptions::invalid_request_exception(
                    format("Cannot create secondary index on partition key column {}",
                            target->as_string()));
        }

        if (cd->type->is_multi_cell()) {
            // NOTICE(sarna): should be lifted after #2962 (indexes on non-frozen collections) is implemented
            // NOTICE(kbraun): don't forget about non-frozen user defined types
            throw exceptions::invalid_request_exception(
                    format("Cannot create secondary index on non-frozen collection or UDT column {}", cd->name_as_text()));
        } else if (cd->type->is_collection()) {
            validate_for_frozen_collection(target);
        } else {
            validate_not_full_index(target);
            validate_is_values_index_if_target_column_not_collection(cd, target);
            validate_target_column_is_map_if_index_involves_keys(cd->type->is_map(), target);
        }
    }

    if (db.existing_index_names(keyspace()).count(_index_name) > 0) {
        if (_if_not_exists) {
            return;
        } else {
            throw exceptions::invalid_request_exception("Index already exists");
        }
    }

    _properties->validate();
}

void create_index_statement::validate_for_local_index(schema_ptr schema) const {
    if (!_raw_targets.empty()) {
            if (const auto* index_pk = std::get_if<std::vector<::shared_ptr<column_identifier::raw>>>(&_raw_targets.front()->value)) {
                auto base_pk_identifiers = *index_pk | boost::adaptors::transformed([&schema] (const ::shared_ptr<column_identifier::raw>& raw_ident) {
                    return raw_ident->prepare_column_identifier(schema);
                });
                auto remaining_base_pk_columns = schema->partition_key_columns();
                auto next_expected_base_column = remaining_base_pk_columns.begin();
                for (const auto& ident : base_pk_identifiers) {
                    auto it = schema->columns_by_name().find(ident->name());
                    if (it == schema->columns_by_name().end() || !it->second->is_partition_key()) {
                        throw exceptions::invalid_request_exception(format("Local index definition must contain full partition key only. Redundant column: {}", ident->to_string()));
                    }
                    if (next_expected_base_column == remaining_base_pk_columns.end()) {
                        throw exceptions::invalid_request_exception(format("Duplicate column definition in local index: {}", it->first));
                    }
                    if (&*next_expected_base_column != it->second) {
                        break;
                    }
                    ++next_expected_base_column;
                }
                if (next_expected_base_column != remaining_base_pk_columns.end()) {
                    throw exceptions::invalid_request_exception(format("Local index definition must contain full partition key only. Missing column: {}", next_expected_base_column->name_as_text()));
                }
                if (_raw_targets.size() == 1) {
                    throw exceptions::invalid_request_exception(format("Local index definition must provide an indexed column, not just partition key"));
                }
            }
        }
        for (unsigned i = 1; i < _raw_targets.size(); ++i) {
            if (std::holds_alternative<index_target::raw::multiple_columns>(_raw_targets[i]->value)) {
                throw exceptions::invalid_request_exception(format("Multi-column index targets are currently only supported for partition key"));
            }
        }
}

void create_index_statement::validate_for_frozen_collection(::shared_ptr<index_target> target) const
{
    if (target->type != index_target::target_type::full) {
        throw exceptions::invalid_request_exception(
                format("Cannot create index on {} of frozen<map> column {}",
                        index_target::index_option(target->type),
                        target->as_string()));
    }
}

void create_index_statement::validate_not_full_index(::shared_ptr<index_target> target) const
{
    if (target->type == index_target::target_type::full) {
        throw exceptions::invalid_request_exception("full() indexes can only be created on frozen collections");
    }
}

void create_index_statement::validate_is_values_index_if_target_column_not_collection(
        const column_definition* cd, ::shared_ptr<index_target> target) const
{
    if (!cd->type->is_collection()
            && target->type != index_target::target_type::values) {
        throw exceptions::invalid_request_exception(
                format("Cannot create index on {} of column {}; only non-frozen collections support {} indexes",
                       index_target::index_option(target->type),
                       target->as_string(),
                       index_target::index_option(target->type)));
    }
}

void create_index_statement::validate_target_column_is_map_if_index_involves_keys(bool is_map, ::shared_ptr<index_target> target) const
{
    if (target->type == index_target::target_type::keys
            || target->type == index_target::target_type::keys_and_values) {
        if (!is_map) {
            throw exceptions::invalid_request_exception(
                    format("Cannot create index on {} of column {} with non-map type",
                           index_target::index_option(target->type), target->as_string()));
        }
    }
}

void create_index_statement::validate_targets_for_multi_column_index(std::vector<::shared_ptr<index_target>> targets) const
{
    if (!_properties->is_custom) {
        if (targets.size() > 2 || (targets.size() == 2 && std::holds_alternative<index_target::single_column>(targets.front()->value))) {
            throw exceptions::invalid_request_exception("Only CUSTOM indexes support multiple columns");
        }
    }
    std::unordered_set<sstring> columns;
    for (auto& target : targets) {
        if (columns.count(target->as_string()) > 0) {
            throw exceptions::invalid_request_exception(format("Duplicate column {} in index target list", target->as_string()));
        }
        columns.emplace(target->as_string());
    }
}

future<::shared_ptr<cql_transport::event::schema_change>>
create_index_statement::announce_migration(service::storage_proxy& proxy, bool is_local_only) {
    if (!service::get_local_storage_service().cluster_supports_indexes()) {
        throw exceptions::invalid_request_exception("Index support is not enabled");
    }
    auto& db = proxy.get_db().local();
    auto schema = db.find_schema(keyspace(), column_family());
    std::vector<::shared_ptr<index_target>> targets;
    for (auto& raw_target : _raw_targets) {
        targets.emplace_back(raw_target->prepare(schema));
    }
    sstring accepted_name = _index_name;
    if (accepted_name.empty()) {
        std::optional<sstring> index_name_root;
        if (targets.size() == 1) {
           index_name_root = targets[0]->as_string();
        } else if ((targets.size() == 2 && std::holds_alternative<index_target::multiple_columns>(targets.front()->value))) {
            index_name_root = targets[1]->as_string();
        }
        accepted_name = db.get_available_index_name(keyspace(), column_family(), index_name_root);
    }
    index_metadata_kind kind;
    index_options_map index_options;
    if (_properties->is_custom) {
        kind = index_metadata_kind::custom;
        index_options = _properties->get_options();
    } else {
        kind = schema->is_compound() ? index_metadata_kind::composites : index_metadata_kind::keys;
    }
    auto index = make_index_metadata(schema, targets, accepted_name, kind, index_options);
    auto existing_index = schema->find_index_noname(index);
    if (existing_index) {
        if (_if_not_exists) {
            return make_ready_future<::shared_ptr<cql_transport::event::schema_change>>(nullptr);
        } else {
            throw exceptions::invalid_request_exception(
                    format("Index {} is a duplicate of existing index {}", index.name(), existing_index.value().name()));
        }
    }
    ++_cql_stats->secondary_index_creates;
    schema_builder builder{schema};
    builder.with_index(index);
    return service::get_local_migration_manager().announce_column_family_update(
            builder.build(), false, {}, is_local_only).then([this]() {
        using namespace cql_transport;
        return make_shared<event::schema_change>(
                event::schema_change::change_type::UPDATED,
                event::schema_change::target_type::TABLE,
                keyspace(),
                column_family());
    });
}

std::unique_ptr<cql3::statements::prepared_statement>
create_index_statement::prepare(database& db, cql_stats& stats) {
    _cql_stats = &stats;
    return std::make_unique<prepared_statement>(make_shared<create_index_statement>(*this));
}

index_metadata create_index_statement::make_index_metadata(schema_ptr schema,
                                                           const std::vector<::shared_ptr<index_target>>& targets,
                                                           const sstring& name,
                                                           index_metadata_kind kind,
                                                           const index_options_map& options)
{
    index_options_map new_options = options;
    auto target_option = secondary_index::target_parser::serialize_targets(targets);
    new_options.emplace(index_target::target_option_name, target_option);

    const auto& first_target = targets.front()->value;
    return index_metadata{name, new_options, kind, index_metadata::is_local_index(std::holds_alternative<index_target::multiple_columns>(first_target))};
}

}

}
<|endoftext|>


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

  Program:   ORFEO Toolbox
  Language:  C++
  Date:      $Date$
  Version:   $Revision$

  Copyright (c) Centre National d'Etudes Spatiales. All rights reserved.
  See OTBCopyright.txt for details.

  This software is distributed WITHOUT ANY WARRANTY; without even
  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
  PURPOSE.  See the above copyright notices for more information.

=========================================================================*/

#include "itkExceptionObject.h"
#include "otbImageFileReader.h"
#include "otbImageFileWriter.h"
#include "otbImage.h"
#include "itkRescaleIntensityImageFilter.h"
#include "otbShiftScaleImageAdaptor.h"

int otbShiftScaleImageAdaptor( int argc, char * argv[] )
{
  const char * inputFilename  = argv[1];
  const char * outputFilename = argv[2];

  typedef double                                                        PixelType;
  typedef otb::Image<PixelType, 2>                                InputImageType;
  typedef otb::Image<PixelType, 2>                                      OutputImageType;
  typedef otb::ImageFileReader< InputImageType  >                       ReaderType;
  typedef otb::ImageFileWriter< OutputImageType >                       WriterType;
  typedef otb::ShiftScaleImageAdaptor<InputImageType, PixelType>        FilterType;
  typedef itk::RescaleIntensityImageFilter<FilterType, OutputImageType> RescalerType;

  FilterType::Pointer   filter   = FilterType::New();
  RescalerType::Pointer rescaler = RescalerType::New();
  ReaderType::Pointer   reader   = ReaderType::New();
  WriterType::Pointer   writer   = WriterType::New();

  filter->SetScale(3.);
  filter->SetShift(10.);

  reader->SetFileName( inputFilename  );

  filter->SetImage( reader->GetOutput() );

  rescaler->SetOutputMinimum( 0 );
  rescaler->SetOutputMaximum( 255 );
  rescaler->SetInput( filter );

  writer->SetFileName( outputFilename );
  writer->SetInput( rescaler->GetOutput() );
  writer->Update();

  return EXIT_SUCCESS;

}

<commit_msg>TEST: output don't really need to be in double here<commit_after>/*=========================================================================

  Program:   ORFEO Toolbox
  Language:  C++
  Date:      $Date$
  Version:   $Revision$

  Copyright (c) Centre National d'Etudes Spatiales. All rights reserved.
  See OTBCopyright.txt for details.

  This software is distributed WITHOUT ANY WARRANTY; without even
  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
  PURPOSE.  See the above copyright notices for more information.

=========================================================================*/

#include "itkExceptionObject.h"
#include "otbImageFileReader.h"
#include "otbImageFileWriter.h"
#include "otbImage.h"
#include "itkRescaleIntensityImageFilter.h"
#include "otbShiftScaleImageAdaptor.h"

int otbShiftScaleImageAdaptor( int argc, char * argv[] )
{
  const char * inputFilename  = argv[1];
  const char * outputFilename = argv[2];

  typedef double                                                        PixelType;
  typedef unsigned char                                           OutputPixelType;
  typedef otb::Image<PixelType, 2>                                InputImageType;
  typedef otb::Image<OutputPixelType, 2>                          OutputImageType;
  typedef otb::ImageFileReader< InputImageType  >                       ReaderType;
  typedef otb::ImageFileWriter< OutputImageType >                       WriterType;
  typedef otb::ShiftScaleImageAdaptor<InputImageType, PixelType>        FilterType;
  typedef itk::RescaleIntensityImageFilter<FilterType, OutputImageType> RescalerType;

  FilterType::Pointer   filter   = FilterType::New();
  RescalerType::Pointer rescaler = RescalerType::New();
  ReaderType::Pointer   reader   = ReaderType::New();
  WriterType::Pointer   writer   = WriterType::New();

  filter->SetScale(3.);
  filter->SetShift(10.);

  reader->SetFileName( inputFilename  );

  filter->SetImage( reader->GetOutput() );

  rescaler->SetOutputMinimum( 0 );
  rescaler->SetOutputMaximum( 255 );
  rescaler->SetInput( filter );

  writer->SetFileName( outputFilename );
  writer->SetInput( rescaler->GetOutput() );
  writer->Update();

  return EXIT_SUCCESS;

}

<|endoftext|>