text:
/**
 * 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.
 */

#include "filesystem.h"

#include "filehandle.h"
#include "common/namenode_info.h"

#include <functional>
#include <limits>
#include <future>
#include <tuple>
#include <pwd.h>

#include <boost/asio/ip/tcp.hpp>

#include "x-platform/syscall.h"

#define FMT_THIS_ADDR "this=" << (void*)this

namespace hdfs {

static const char kNamenodeProtocol[] = "org.apache.hadoop.hdfs.protocol.ClientProtocol";
static const int kNamenodeProtocolVersion = 1;

using boost::asio::ip::tcp;

static constexpr uint16_t kDefaultPort = 8020;

// forward declarations
const std::string get_effective_user_name(const std::string &);

uint32_t FileSystem::GetDefaultFindMaxDepth() {
  return std::numeric_limits<uint32_t>::max();
}

uint16_t FileSystem::GetDefaultPermissionMask() {
  return 0755;
}

Status FileSystem::CheckValidPermissionMask(uint16_t permissions) {
  if (permissions > 01777) {
    std::stringstream errormsg;
    errormsg << "CheckValidPermissionMask: argument 'permissions' is " << std::oct
        << std::showbase << permissions << " (should be between 0 and 01777)";
    return Status::InvalidArgument(errormsg.str().c_str());
  }
  return Status::OK();
}

Status FileSystem::CheckValidReplication(uint16_t replication) {
  if (replication < 1 || replication > 512) {
    std::stringstream errormsg;
    errormsg << "CheckValidReplication: argument 'replication' is "
        << replication << " (should be between 1 and 512)";
    return Status::InvalidArgument(errormsg.str().c_str());
  }
  return Status::OK();
}

FileSystem::~FileSystem() {}

/*****************************************************************************
 *                    FILESYSTEM BASE CLASS
 ****************************************************************************/

FileSystem *FileSystem::New(
    IoService *&io_service, const std::string &user_name, const Options &options) {
  return new FileSystemImpl(io_service, user_name, options);
}

FileSystem *FileSystem::New(
    std::shared_ptr<IoService> io_service, const std::string &user_name, const Options &options) {
  return new FileSystemImpl(io_service, user_name, options);
}

FileSystem *FileSystem::New() {
  // No, this pointer won't be leaked.  The FileSystem takes ownership.
  std::shared_ptr<IoService> io_service = IoService::MakeShared();
  if(!io_service)
    return nullptr;
  int thread_count = io_service->InitDefaultWorkers();
  if(thread_count < 1)
    return nullptr;

  std::string user_name = get_effective_user_name("");
  Options options;
  return new FileSystemImpl(io_service, user_name, options);
}

/*****************************************************************************
 *                    FILESYSTEM IMPLEMENTATION
 ****************************************************************************/

struct FileSystemImpl::FindSharedState {
  //Name pattern (can have wild-cards) to find
  const std::string name;
  //Maximum depth to recurse after the end of path is reached.
  //Can be set to 0 for pure path globbing and ignoring name pattern entirely.
  const uint32_t maxdepth;
  //Vector of all sub-directories from the path argument (each can have wild-cards)
  std::vector<std::string> dirs;
  //Callback from Find
  const std::function<bool(const Status &, const std::vector<StatInfo> &, bool)> handler;
  //outstanding_requests is incremented once for every GetListing call.
  std::atomic<uint64_t> outstanding_requests;
  //Boolean needed to abort all recursion on error or on user command
  std::atomic<bool> aborted;
  //Shared variables will need protection with a lock
  std::mutex lock;
  FindSharedState(const std::string path_, const std::string name_, const uint32_t maxdepth_,
              const std::function<bool(const Status &, const std::vector<StatInfo> &, bool)> handler_,
              uint64_t outstanding_recuests_, bool aborted_)
      : name(name_),
        maxdepth(maxdepth_),
        handler(handler_),
        outstanding_requests(outstanding_recuests_),
        aborted(aborted_),
        lock() {
    //Constructing the list of sub-directories
    std::stringstream ss(path_);
    if(path_.back() != '/'){
      ss << "/";
    }
    for (std::string token; std::getline(ss, token, '/'); ) {
      dirs.push_back(token);
    }
  }
};

struct FileSystemImpl::FindOperationalState {
  const std::string path;
  const uint32_t depth;
  const bool search_path;
  FindOperationalState(const std::string path_, const uint32_t depth_, const bool search_path_)
      : path(path_),
        depth(depth_),
        search_path(search_path_) {
  }
};

const std::string get_effective_user_name(const std::string &user_name) {
  if (!user_name.empty())
    return user_name;

  // If no user name was provided, try the HADOOP_USER_NAME and USER environment
  //    variables
  const char * env = getenv("HADOOP_USER_NAME");
  if (env) {
    return env;
  }

  env = getenv("USER");
  if (env) {
    return env;
  }

  // If running on POSIX, use the currently logged in user
#if defined(_POSIX_VERSION)
  uid_t uid = geteuid();
  struct passwd *pw = getpwuid(uid);
  if (pw && pw->pw_name)
  {
    return pw->pw_name;
  }
#endif

  return "unknown_user";
}

FileSystemImpl::FileSystemImpl(IoService *&io_service, const std::string &user_name, const Options &options) :
     io_service_(io_service), options_(options),
     client_name_(GetRandomClientName()),
     nn_(
       io_service_, options, client_name_,
       get_effective_user_name(user_name), kNamenodeProtocol,
       kNamenodeProtocolVersion
     ),
     bad_node_tracker_(std::make_shared<BadDataNodeTracker>()),
     event_handlers_(std::make_shared<LibhdfsEvents>())
{

  LOG_DEBUG(kFileSystem, << "FileSystemImpl::FileSystemImpl("
                         << FMT_THIS_ADDR << ") called");

  // Poor man's move
  io_service = nullptr;

  unsigned int running_workers = 0;
  if(options.io_threads_ < 1) {
    LOG_DEBUG(kFileSystem, << "FileSystemImpl::FileSystemImpl Initializing default number of worker threads");
    running_workers = io_service_->InitDefaultWorkers();
  } else {
    LOG_DEBUG(kFileSystem, << "FileSystemImpl::FileSystenImpl Initializing " << options_.io_threads_ << " worker threads.");
    running_workers = io_service->InitWorkers(options_.io_threads_);
  }

  if(running_workers < 1) {
    LOG_WARN(kFileSystem, << "FileSystemImpl::FileSystemImpl was unable to start worker threads");
  }
}

FileSystemImpl::FileSystemImpl(std::shared_ptr<IoService> io_service, const std::string& user_name, const Options &options) :
     io_service_(io_service), options_(options),
     client_name_(GetRandomClientName()),
     nn_(
       io_service_, options, client_name_,
       get_effective_user_name(user_name), kNamenodeProtocol,
       kNamenodeProtocolVersion
     ),
     bad_node_tracker_(std::make_shared<BadDataNodeTracker>()),
     event_handlers_(std::make_shared<LibhdfsEvents>())
{
  LOG_DEBUG(kFileSystem, << "FileSystemImpl::FileSystemImpl("
                         << FMT_THIS_ADDR << ", shared IoService@" << io_service_.get() << ") called");
  int worker_thread_count = io_service_->GetWorkerThreadCount();
  if(worker_thread_count < 1) {
    LOG_WARN(kFileSystem, << "FileSystemImpl::FileSystemImpl IoService provided doesn't have any worker threads. "
                          << "It needs at least 1 worker to connect to an HDFS cluster.")
  } else {
    LOG_DEBUG(kFileSystem, << "FileSystemImpl::FileSystemImpl using " << worker_thread_count << " worker threads.");
  }
}

FileSystemImpl::~FileSystemImpl() {
  LOG_TRACE(kFileSystem, << "FileSystemImpl::~FileSystemImpl("
                         << FMT_THIS_ADDR << ") called");

  /**
   * Note: IoService must be stopped before getting rid of worker threads.
   * Once worker threads are joined and deleted the service can be deleted.
   **/
  io_service_->Stop();
}

void FileSystemImpl::Connect(const std::string &server,
                             const std::string &service,
                             const std::function<void(const Status &, FileSystem * fs)> &handler) {
  LOG_INFO(kFileSystem, << "FileSystemImpl::Connect(" << FMT_THIS_ADDR
                        << ", server=" << server << ", service="
                        << service << ") called");
  connect_callback_.SetCallback(handler);

  /* IoService::New can return nullptr */
  if (!io_service_) {
    handler (Status::Error("Null IoService"), this);
  }

  // DNS lookup here for namenode(s)
  std::vector<ResolvedNamenodeInfo> resolved_namenodes;

  auto name_service = options_.services.find(server);
  if(name_service != options_.services.end()) {
    cluster_name_ = name_service->first;
    resolved_namenodes = BulkResolve(io_service_, name_service->second);
  } else {
    cluster_name_ = server + ":" + service;

    // tmp namenode info just to get this in the right format for BulkResolve
    NamenodeInfo tmp_info;
    try {
      tmp_info.uri = URI::parse_from_string("hdfs://" + cluster_name_);
    } catch (const uri_parse_error& e) {
      LOG_ERROR(kFileSystem, << "Unable to use URI for cluster " << cluster_name_);
      handler(Status::Error(("Invalid namenode " + cluster_name_ + " in config").c_str()), this);
    }

    resolved_namenodes = BulkResolve(io_service_, {tmp_info});
  }

  for(unsigned int i=0;i<resolved_namenodes.size();i++) {
    LOG_DEBUG(kFileSystem, << "Resolved Namenode");
    LOG_DEBUG(kFileSystem, << resolved_namenodes[i].str());
  }

  nn_.Connect(cluster_name_, /*server, service*/ resolved_namenodes, [this](const Status & s) {
    connect_callback_.GetCallback()(s, this);
  });
}

void FileSystemImpl::ConnectToDefaultFs(const std::function<void(const Status &, FileSystem *)> &handler) {
  std::string scheme = options_.defaultFS.get_scheme();
  if (strcasecmp(scheme.c_str(), "hdfs") != 0) {
    std::string error_message;
    error_message += "defaultFS of [" + options_.defaultFS.str() + "] is not supported";
    handler(Status::InvalidArgument(error_message.c_str()), nullptr);
    return;
  }

  std::string host = options_.defaultFS.get_host();
  if (host.empty()) {
    handler(Status::InvalidArgument("defaultFS must specify a hostname"), nullptr);
    return;
  }

  int16_t port = options_.defaultFS.get_port_or_default(kDefaultPort);
  std::string port_as_string = std::to_string(port);

  Connect(host, port_as_string, handler);
}

int FileSystemImpl::AddWorkerThread() {
  LOG_DEBUG(kFileSystem, << "FileSystemImpl::AddWorkerThread("
                                  << FMT_THIS_ADDR << ") called."
                                  << " Existing thread count = " << WorkerThreadCount());

  if(!io_service_)
    return -1;

  io_service_->AddWorkerThread();
  return 1;
}

int FileSystemImpl::WorkerThreadCount() {
  if(!io_service_) {
    return -1;
  } else {
    return io_service_->GetWorkerThreadCount();
  }
}

bool FileSystemImpl::CancelPendingConnect() {
  if(connect_callback_.IsCallbackAccessed()) {
    // Temp fix for failover hangs, allow CancelPendingConnect to be called so it can push a flag through the RPC engine
    LOG_DEBUG(kFileSystem, << "FileSystemImpl@" << this << "::CancelPendingConnect called after Connect completed");
    return nn_.CancelPendingConnect();
  }

  if(!connect_callback_.IsCallbackSet()) {
    LOG_DEBUG(kFileSystem, << "FileSystemImpl@" << this << "::CancelPendingConnect called before Connect started");
    return false;
  }

  // First invoke callback, then do proper teardown in RpcEngine and RpcConnection
  ConnectCallback noop_callback = [](const Status &stat, FileSystem *fs) {
    LOG_DEBUG(kFileSystem, << "Dummy callback invoked for canceled FileSystem@" << fs << "::Connect with status: " << stat.ToString());
  };

  bool callback_swapped = false;
  ConnectCallback original_callback = connect_callback_.AtomicSwapCallback(noop_callback, callback_swapped);

  if(callback_swapped) {
    // Take original callback and invoke it as if it was canceled.
    LOG_DEBUG(kFileSystem, << "Swapped in dummy callback.  Invoking connect callback with canceled status.");
    std::function<void(void)> wrapped_callback = [original_callback, this](){
      // handling code expected to check status before dereferenceing 'this'
      original_callback(Status::Canceled(), this);
    };
    io_service_->PostTask(wrapped_callback);
  } else {
    LOG_INFO(kFileSystem, << "Unable to cancel FileSystem::Connect.  It hasn't been invoked yet or may have already completed.")
    return false;
  }

  // Now push cancel down to clean up where possible and make sure the RpcEngine
  // won't try to do retries in the background.  The rest of the memory cleanup
  // happens when this FileSystem is deleted by the user.
  return nn_.CancelPendingConnect();
}

void FileSystemImpl::Open(
    const std::string &path,
    const std::function<void(const Status &, FileHandle *)> &handler) {
  LOG_DEBUG(kFileSystem, << "FileSystemImpl::Open("
                                 << FMT_THIS_ADDR << ", path="
                                 << path << ") called");

  nn_.GetBlockLocations(path, 0, std::numeric_limits<int64_t>::max(), [this, path, handler](const Status &stat, std::shared_ptr<const struct FileInfo> file_info) {
    if(!stat.ok()) {
      LOG_DEBUG(kFileSystem, << "FileSystemImpl::Open failed to get block locations. status=" << stat.ToString());
      if(stat.get_server_exception_type() == Status::kStandbyException) {
        LOG_DEBUG(kFileSystem, << "Operation not allowed on standby datanode");
      }
    }
    handler(stat, stat.ok() ? new FileHandleImpl(cluster_name_, path, io_service_, client_name_, file_info, bad_node_tracker_, event_handlers_)
                            : nullptr);
  });
}

BlockLocation LocatedBlockToBlockLocation(const hadoop::hdfs::LocatedBlockProto & locatedBlock)
{
  BlockLocation result;

  result.setCorrupt(locatedBlock.corrupt());
  result.setOffset(locatedBlock.offset());

  std::vector<DNInfo> dn_info;
  dn_info.reserve(locatedBlock.locs_size());
  for (const hadoop::hdfs::DatanodeInfoProto & datanode_info: locatedBlock.locs()) {
    const hadoop::hdfs::DatanodeIDProto &id = datanode_info.id();
    DNInfo newInfo;
    if (id.has_ipaddr())
        newInfo.setIPAddr(id.ipaddr());
    if (id.has_hostname())
        newInfo.setHostname(id.hostname());
    if (id.has_xferport())
        newInfo.setXferPort(id.xferport());
    if (id.has_infoport())
        newInfo.setInfoPort(id.infoport());
    if (id.has_ipcport())
        newInfo.setIPCPort(id.ipcport());
    if (id.has_infosecureport())
      newInfo.setInfoSecurePort(id.infosecureport());
    if (datanode_info.has_location())
      newInfo.setNetworkLocation(datanode_info.location());
    dn_info.push_back(newInfo);
  }
  result.setDataNodes(dn_info);

  if (locatedBlock.has_b()) {
    const hadoop::hdfs::ExtendedBlockProto & b=locatedBlock.b();
    result.setLength(b.numbytes());
  }

  return result;
}

void FileSystemImpl::GetBlockLocations(const std::string & path, uint64_t offset, uint64_t length,
  const std::function<void(const Status &, std::shared_ptr<FileBlockLocation> locations)> handler)
{
  LOG_DEBUG(kFileSystem, << "FileSystemImpl::GetBlockLocations("
                                 << FMT_THIS_ADDR << ", path="
                                 << path << ") called");

  //Protobuf gives an error 'Negative value is not supported'
  //if the high bit is set in uint64 in GetBlockLocations
  if (IsHighBitSet(offset)) {
    handler(Status::InvalidArgument("GetBlockLocations: argument 'offset' cannot have high bit set"), nullptr);
    return;
  }
  if (IsHighBitSet(length)) {
    handler(Status::InvalidArgument("GetBlockLocations: argument 'length' cannot have high bit set"), nullptr);
    return;
  }

  auto conversion = [handler](const Status & status, std::shared_ptr<const struct FileInfo> fileInfo) {
    if (status.ok()) {
      auto result = std::make_shared<FileBlockLocation>();

      result->setFileLength(fileInfo->file_length_);
      result->setLastBlockComplete(fileInfo->last_block_complete_);
      result->setUnderConstruction(fileInfo->under_construction_);

      std::vector<BlockLocation> blocks;
      for (const hadoop::hdfs::LocatedBlockProto & locatedBlock: fileInfo->blocks_) {
          auto newLocation = LocatedBlockToBlockLocation(locatedBlock);
          blocks.push_back(newLocation);
      }
      result->setBlockLocations(blocks);

      handler(status, result);
    } else {
      handler(status, std::shared_ptr<FileBlockLocation>());
    }
  };

  nn_.GetBlockLocations(path, offset, length, conversion);
}

void FileSystemImpl::GetPreferredBlockSize(const std::string &path,
    const std::function<void(const Status &, const uint64_t &)> &handler) {
  LOG_DEBUG(kFileSystem, << "FileSystemImpl::GetPreferredBlockSize("
                                 << FMT_THIS_ADDR << ", path="
                                 << path << ") called");

  nn_.GetPreferredBlockSize(path, handler);
}

void FileSystemImpl::SetReplication(const std::string & path, int16_t replication, std::function<void(const Status &)> handler) {
  LOG_DEBUG(kFileSystem,
      << "FileSystemImpl::SetReplication(" << FMT_THIS_ADDR << ", path=" << path <<
      ", replication=" << replication << ") called");

  if (path.empty()) {
    handler(Status::InvalidArgument("SetReplication: argument 'path' cannot be empty"));
    return;
  }
  Status replStatus = FileSystem::CheckValidReplication(replication);
  if (!replStatus.ok()) {
    handler(replStatus);
    return;
  }

  nn_.SetReplication(path, replication, handler);
}

void FileSystemImpl::SetTimes(const std::string & path, uint64_t mtime, uint64_t atime,
    std::function<void(const Status &)> handler) {
  LOG_DEBUG(kFileSystem,
      << "FileSystemImpl::SetTimes(" << FMT_THIS_ADDR << ", path=" << path <<
      ", mtime=" << mtime << ", atime=" << atime << ") called");

  if (path.empty()) {
    handler(Status::InvalidArgument("SetTimes: argument 'path' cannot be empty"));
    return;
  }

  nn_.SetTimes(path, mtime, atime, handler);
}

void FileSystemImpl::GetFileInfo(
    const std::string &path,
    const std::function<void(const Status &, const StatInfo &)> &handler) {
  LOG_DEBUG(kFileSystem, << "FileSystemImpl::GetFileInfo("
                                 << FMT_THIS_ADDR << ", path="
                                 << path << ") called");

  nn_.GetFileInfo(path, handler);
}

void FileSystemImpl::GetContentSummary(
    const std::string &path,
    const std::function<void(const Status &, const ContentSummary &)> &handler) {
  LOG_DEBUG(kFileSystem, << "FileSystemImpl::GetContentSummary("
                                 << FMT_THIS_ADDR << ", path="
                                 << path << ") called");

  nn_.GetContentSummary(path, handler);
}

void FileSystemImpl::GetFsStats(
    const std::function<void(const Status &, const FsInfo &)> &handler) {
  LOG_DEBUG(kFileSystem,
      << "FileSystemImpl::GetFsStats(" << FMT_THIS_ADDR << ") called");

  nn_.GetFsStats(handler);
}

/**
 * Helper function for recursive GetListing calls.
 *
 * Some compilers don't like recursive lambdas, so we make the lambda call a
 * method, which in turn creates a lambda calling itself.
 */
void FileSystemImpl::GetListingShim(const Status &stat, const std::vector<StatInfo> & stat_infos, bool has_more,
                      std::string path, const std::function<bool(const Status &, const std::vector<StatInfo> &, bool)> &handler) {
  bool has_next = !stat_infos.empty();
  bool get_more = handler(stat, stat_infos, has_more && has_next);
  if (get_more && has_more && has_next ) {
    auto callback = [this, path, handler](const Status &stat, const std::vector<StatInfo> & stat_infos, bool has_more) {
      GetListingShim(stat, stat_infos, has_more, path, handler);
    };

    std::string last = stat_infos.back().path;
    nn_.GetListing(path, callback, last);
  }
}

void FileSystemImpl::GetListing(
    const std::string &path,
    const std::function<bool(const Status &, const std::vector<StatInfo> &, bool)> &handler) {
  LOG_DEBUG(kFileSystem, << "FileSystemImpl::GetListing("
                                 << FMT_THIS_ADDR << ", path="
                                 << path << ") called");
  std::string path_fixed = path;
  if(path.back() != '/'){
    path_fixed += "/";
  }
  // Caputure the state and push it into the shim
  auto callback = [this, path_fixed, handler](const Status &stat, const std::vector<StatInfo> & stat_infos, bool has_more) {
    GetListingShim(stat, stat_infos, has_more, path_fixed, handler);
  };

  nn_.GetListing(path_fixed, callback);
}

void FileSystemImpl::Mkdirs(const std::string & path, uint16_t permissions, bool createparent,
    std::function<void(const Status &)> handler) {
  LOG_DEBUG(kFileSystem,
      << "FileSystemImpl::Mkdirs(" << FMT_THIS_ADDR << ", path=" << path <<
      ", permissions=" << permissions << ", createparent=" << createparent << ") called");

  if (path.empty()) {
    handler(Status::InvalidArgument("Mkdirs: argument 'path' cannot be empty"));
    return;
  }

  Status permStatus = FileSystem::CheckValidPermissionMask(permissions);
  if (!permStatus.ok()) {
    handler(permStatus);
    return;
  }

  nn_.Mkdirs(path, permissions, createparent, handler);
}

void FileSystemImpl::Delete(const std::string &path, bool recursive,
    const std::function<void(const Status &)> &handler) {
  LOG_DEBUG(kFileSystem,
      << "FileSystemImpl::Delete(" << FMT_THIS_ADDR << ", path=" << path << ", recursive=" << recursive << ") called");

  if (path.empty()) {
    handler(Status::InvalidArgument("Delete: argument 'path' cannot be empty"));
    return;
  }

  nn_.Delete(path, recursive, handler);
}

void FileSystemImpl::Rename(const std::string &oldPath, const std::string &newPath,
    const std::function<void(const Status &)> &handler) {
  LOG_DEBUG(kFileSystem,
      << "FileSystemImpl::Rename(" << FMT_THIS_ADDR << ", oldPath=" << oldPath << ", newPath=" << newPath << ") called");

  if (oldPath.empty()) {
    handler(Status::InvalidArgument("Rename: argument 'oldPath' cannot be empty"));
    return;
  }

  if (newPath.empty()) {
    handler(Status::InvalidArgument("Rename: argument 'newPath' cannot be empty"));
    return;
  }

  nn_.Rename(oldPath, newPath, handler);
}

void FileSystemImpl::SetPermission(const std::string & path,
    uint16_t permissions, const std::function<void(const Status &)> &handler) {
  LOG_DEBUG(kFileSystem,
      << "FileSystemImpl::SetPermission(" << FMT_THIS_ADDR << ", path=" << path << ", permissions=" << permissions << ") called");

  if (path.empty()) {
    handler(Status::InvalidArgument("SetPermission: argument 'path' cannot be empty"));
    return;
  }
  Status permStatus = FileSystem::CheckValidPermissionMask(permissions);
  if (!permStatus.ok()) {
    handler(permStatus);
    return;
  }

  nn_.SetPermission(path, permissions, handler);
}

void FileSystemImpl::SetOwner(const std::string & path, const std::string & username,
    const std::string & groupname, const std::function<void(const Status &)> &handler) {
  LOG_DEBUG(kFileSystem,
      << "FileSystemImpl::SetOwner(" << FMT_THIS_ADDR << ", path=" << path << ", username=" << username << ", groupname=" << groupname << ") called");

  if (path.empty()) {
    handler(Status::InvalidArgument("SetOwner: argument 'path' cannot be empty"));
    return;
  }

  nn_.SetOwner(path, username, groupname, handler);
}

/**
 * Helper function for recursive Find calls.
 *
 * Some compilers don't like recursive lambdas, so we make the lambda call a
 * method, which in turn creates a lambda calling itself.
 *
 * ***High-level explanation***
 *
 * Since we are allowing to use wild cards in both path and name, we start by expanding the path first.
 * Boolean search_path is set to true when we search for the path and false when we search for the name.
 * When we search for the path we break the given path pattern into sub-directories. Starting from the
 * first sub-directory we list them one-by-one and recursively continue into directories that matched the
 * path pattern at the current depth. Directories that are large will be requested to continue sending
 * the results. We keep track of the current depth within the path pattern in the 'depth' variable.
 * This continues recursively until the depth reaches the end of the path. Next that we start matching
 * the name pattern. All directories that we find we recurse now, and all names that match the given name
 * pattern are being stored in outputs and later sent back to the user.
 */
void FileSystemImpl::FindShim(const Status &stat, const std::vector<StatInfo> & stat_infos, bool directory_has_more,
                      std::shared_ptr<FindOperationalState> operational_state, std::shared_ptr<FindSharedState> shared_state) {
  //We buffer the outputs then send them back at the end
  std::vector<StatInfo> outputs;
  //Return on error
  if(!stat.ok()){
    std::lock_guard<std::mutex> find_lock(shared_state->lock);
    //We send true becuase we do not want the user code to exit before all our requests finished
    shared_state->handler(stat, outputs, true);
    shared_state->aborted = true;
  }
  if(!shared_state->aborted){
    //User did not abort the operation
    if (directory_has_more) {
      //Directory is large and has more results
      //We launch another async call to get more results
      shared_state->outstanding_requests++;
      auto callback = [this, operational_state, shared_state](const Status &stat, const std::vector<StatInfo> & stat_infos, bool has_more) {
        FindShim(stat, stat_infos, has_more, operational_state, shared_state);
      };
      std::string last = stat_infos.back().path;
      nn_.GetListing(operational_state->path, callback, last);
    }
    if(operational_state->search_path && operational_state->depth < shared_state->dirs.size() - 1){
      //We are searching for the path and did not reach the end of the path yet
      for (StatInfo const& si : stat_infos) {
        //If we are at the last depth and it matches both path and name, we need to output it.
        if (operational_state->depth == shared_state->dirs.size() - 2
            && XPlatform::Syscall::FnMatch(shared_state->dirs[operational_state->depth + 1], si.path)
            && XPlatform::Syscall::FnMatch(shared_state->name, si.path)) {
          outputs.push_back(si);
        }
        //Skip if not directory
        if(si.file_type != StatInfo::IS_DIR) {
          continue;
        }
        //Checking for a match with the path at the current depth
        if(XPlatform::Syscall::FnMatch(shared_state->dirs[operational_state->depth + 1], si.path)) {
          //Launch a new requests for every matched directory
          shared_state->outstanding_requests++;
          auto callback = [this, si, operational_state, shared_state](const Status &stat, const std::vector<StatInfo> & stat_infos, bool has_more) {
            std::shared_ptr<FindOperationalState> new_current_state = std::make_shared<FindOperationalState>(si.full_path, operational_state->depth + 1, true);  //true because searching for the path
            FindShim(stat, stat_infos, has_more, new_current_state, shared_state);
          };
          nn_.GetListing(si.full_path, callback);
        }
      }
    }
    else if(shared_state->maxdepth > operational_state->depth - shared_state->dirs.size() + 1){
      //We are searching for the name now and maxdepth has not been reached
      for (StatInfo const& si : stat_infos) {
        //Launch a new request for every directory
        if(si.file_type == StatInfo::IS_DIR) {
          shared_state->outstanding_requests++;
          auto callback = [this, si, operational_state, shared_state](const Status &stat, const std::vector<StatInfo> & stat_infos, bool has_more) {
            std::shared_ptr<FindOperationalState> new_current_state = std::make_shared<FindOperationalState>(si.full_path, operational_state->depth + 1, false); //false because searching for the name
            FindShim(stat, stat_infos, has_more, new_current_state, shared_state);
          };
          nn_.GetListing(si.full_path, callback);
        }
        //All names that match the specified name are saved to outputs
        if(XPlatform::Syscall::FnMatch(shared_state->name, si.path)) {
          outputs.push_back(si);
        }
      }
    }
  }
  //This section needs a lock to make sure we return the final chunk only once
  //and no results are sent after aborted is set
  std::lock_guard<std::mutex> find_lock(shared_state->lock);
  //Decrement the counter once since we are done with this chunk
  shared_state->outstanding_requests--;
  if(shared_state->outstanding_requests == 0){
    //Send the outputs back to the user and notify that this is the final chunk
    shared_state->handler(stat, outputs, false);
  } else {
    //There will be more results and we are not aborting
    if (outputs.size() > 0 && !shared_state->aborted){
      //Send the outputs back to the user and notify that there is more
      bool user_wants_more = shared_state->handler(stat, outputs, true);
      if(!user_wants_more) {
        //Abort if user doesn't want more
        shared_state->aborted = true;
      }
    }
  }
}

void FileSystemImpl::Find(
    const std::string &path, const std::string &name, const uint32_t maxdepth,
    const std::function<bool(const Status &, const std::vector<StatInfo> &, bool)> &handler) {
  LOG_DEBUG(kFileSystem, << "FileSystemImpl::Find("
                                 << FMT_THIS_ADDR << ", path="
                                 << path << ", name="
                                 << name << ") called");

  //Populating the operational state, which includes:
  //current search path, depth within the path, and the indication that we are currently searching for a path (not name yet).
  std::shared_ptr<FindOperationalState> operational_state  = std::make_shared<FindOperationalState>(path, 0, true);
  //Populating the shared state, which includes:
  //vector of sub-directories constructed from path, name to search, handler to use for result returning, outstanding_requests counter, and aborted flag.
  std::shared_ptr<FindSharedState> shared_state = std::make_shared<FindSharedState>(path, name, maxdepth, handler, 1, false);
  auto callback = [this, operational_state, shared_state](const Status &stat, const std::vector<StatInfo> & stat_infos, bool directory_has_more) {
    FindShim(stat, stat_infos, directory_has_more, operational_state, shared_state);
  };
  nn_.GetListing("/", callback);
}

void FileSystemImpl::CreateSnapshot(const std::string &path,
    const std::string &name,
    const std::function<void(const Status &)> &handler) {
  LOG_DEBUG(kFileSystem,
      << "FileSystemImpl::CreateSnapshot(" << FMT_THIS_ADDR << ", path=" << path << ", name=" << name << ") called");

  if (path.empty()) {
    handler(Status::InvalidArgument("CreateSnapshot: argument 'path' cannot be empty"));
    return;
  }

  nn_.CreateSnapshot(path, name, handler);
}

void FileSystemImpl::DeleteSnapshot(const std::string &path,
    const std::string &name,
    const std::function<void(const Status &)> &handler) {
  LOG_DEBUG(kFileSystem,
      << "FileSystemImpl::DeleteSnapshot(" << FMT_THIS_ADDR << ", path=" << path << ", name=" << name << ") called");

  if (path.empty()) {
    handler(Status::InvalidArgument("DeleteSnapshot: argument 'path' cannot be empty"));
    return;
  }
  if (name.empty()) {
    handler(Status::InvalidArgument("DeleteSnapshot: argument 'name' cannot be empty"));
    return;
  }

  nn_.DeleteSnapshot(path, name, handler);
}

void FileSystemImpl::RenameSnapshot(const std::string &path,
    const std::string &old_name, const std::string &new_name,
    const std::function<void(const Status &)> &handler) {
  LOG_DEBUG(kFileSystem,
    << "FileSystemImpl::RenameSnapshot(" << FMT_THIS_ADDR << ", path=" << path <<
    ", old_name=" << old_name << ", new_name=" << new_name << ") called");

  if (path.empty()) {
    handler(Status::InvalidArgument("RenameSnapshot: argument 'path' cannot be empty"));
    return;
  }
  if (old_name.empty()) {
    handler(Status::InvalidArgument("RenameSnapshot: argument 'old_name' cannot be empty"));
    return;
  }
  if (new_name.empty()) {
    handler(Status::InvalidArgument("RenameSnapshot: argument 'new_name' cannot be empty"));
    return;
  }

  nn_.RenameSnapshot(path, old_name, new_name, handler);
}

void FileSystemImpl::AllowSnapshot(const std::string &path,
    const std::function<void(const Status &)> &handler) {
  LOG_DEBUG(kFileSystem,
      << "FileSystemImpl::AllowSnapshot(" << FMT_THIS_ADDR << ", path=" << path << ") called");

  if (path.empty()) {
    handler(Status::InvalidArgument("AllowSnapshot: argument 'path' cannot be empty"));
    return;
  }

  nn_.AllowSnapshot(path, handler);
}

void FileSystemImpl::DisallowSnapshot(const std::string &path,
    const std::function<void(const Status &)> &handler) {
  LOG_DEBUG(kFileSystem,
      << "FileSystemImpl::DisallowSnapshot(" << FMT_THIS_ADDR << ", path=" << path << ") called");

  if (path.empty()) {
    handler(Status::InvalidArgument("DisallowSnapshot: argument 'path' cannot be empty"));
    return;
  }

  nn_.DisallowSnapshot(path, handler);
}

void FileSystemImpl::SetFsEventCallback(fs_event_callback callback) {
  if (event_handlers_) {
    event_handlers_->set_fs_callback(callback);
    nn_.SetFsEventCallback(callback);
  }
}

std::shared_ptr<LibhdfsEvents> FileSystemImpl::get_event_handlers() {
  return event_handlers_;
}

Options FileSystemImpl::get_options() {
  return options_;
}

std::string FileSystemImpl::get_cluster_name() {
  return cluster_name_;
}

}

ext: cc

max_stars_repo_name: sungpeo/hadoop

max_issues_repo_name: sungpeo/hadoop

max_forks_repo_name: sungpeo/hadoop


text:
//////////////////////////////////////////////////////////////////////////////
// detail::multi_array::serialization::serialize.hpp                        //
//                                                                          //
//  (C) Copyright 2009 Erwann Rogard                                        //
//  Use, modification and distribution are subject to the                   //
//  Boost Software License, Version 1.0. (See accompanying file             //
//  LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)        //
//////////////////////////////////////////////////////////////////////////////
#ifndef BOOST_STATISTICS_DETAIL_MULTI_ARRAY_SERIALIZATION_SERIALIZE_HPP_ER_2009
#define BOOST_STATISTICS_DETAIL_MULTI_ARRAY_SERIALIZATION_SERIALIZE_HPP_ER_2009
#include <boost/statistics/detail/multi_array/serialization/save.hpp>
#include <boost/statistics/detail/multi_array/serialization/load.hpp>
#include <boost/serialization/split_free.hpp>

namespace boost{ namespace serialization{

    template<class Archive,typename T>
    inline void serialize(
        Archive & ar,
        boost::multi_array<T,2>& t,
        const unsigned int file_version
    )
    {
        split_free(ar, t, file_version);
    }

}}

#endif

ext: hpp


text:
/*****************************************************************************
 * random.cpp        Blitz++ random numbers example
 */

#include <random/uniform.h>
#include <blitz/numinquire.h>
#include <time.h>
#include <iostream>
#include <iomanip>

using namespace ranlib;
using namespace blitz;

// workaround for broken streams in Compaq cxx, can't handle long double
#if defined(__DECCXX)
#define LD_HACK(x) static_cast<double>(x)
#else
#define LD_HACK(x) x
#endif

template<typename T>
void printRandoms()
{
  Uniform<T> x;
  //x.seed((unsigned int)time(0));
  x.seed(5);
  int N=5;
  for (int i = 0; i < N; ++i)
    cout << setprecision(digits10(T())) << LD_HACK(x.random()) << endl;

  cout << endl;
}

int main()
{
// test get/set state interface
  Uniform<double> x;
  Uniform<double>::T_state S = x.getState();
  x.setState(S);
  std::string str = x.getStateString();
  x.setState(str);

  cout << "Some random float: " << endl;
  printRandoms<float>();

  cout << "Some random doubles: " << endl;
  printRandoms<double>();

  cout << "Some random long doubles: " << endl;
  printRandoms<long double>();

  return 0;
}

ext: cpp

max_stars_repo_name: fraguela/depspawn

max_issues_repo_name: MSV-Project/IBAMR

max_forks_repo_name: MSV-Project/IBAMR


text: #include <boost/fusion/container/generation/ignore.hpp>

max_stars_repo_name: miathedev/BoostForArduino

max_issues_repo_name: miathedev/BoostForArduino

max_forks_repo_name: miathedev/BoostForArduino


text:
#include <iostream>
#include <boost/asio.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>

boost::asio::io_service io;
void work_for_io_service(const boost::system::error_code& /*e*/)
{
  std::cout << "Non-blocking wait() \n";
}

int main()
{
    // work_for_io_service() will be called
  // when async operation (async_wait()) finishes
  // note: Though the async_wait() immediately returns
  //       but the callback function will be called when time expires

  boost::asio::deadline_timer timer(io, boost::posix_time::seconds(1));
  timer.async_wait(&work_for_io_service);

  std::cout << " If we see this before the callback function, we know async_wait() returns immediately.\n This confirms async_wait() is non-blocking call!\n";

  // the callback function, work_for_io_service(), will be called
  // from the thread where io.run() is running.
  io.run();
  return 0;
}

max_stars_repo_name: littletree-github/scrath

max_issues_repo_name: littletree-github/scrath

max_forks_repo_name: littletree-github/scrath


text:
//
// Copyright (c) 2012-2016 Krzysztof Jusiak (krzysztof at jusiak dot net)
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
//<-
#include <cassert>
#include <memory>
//->
#include <boost/di.hpp>

namespace di = boost::di;

template <class, class TScope>
class session_scope {
  class session_guard {
   public:
    explicit session_guard(bool& guard) : guard_(guard) { guard = true; }
    ~session_guard() { guard_ = false; }

   private:
    bool& guard_;
  };

 public:
  template <class TExpected, class TGiven>
  class scope {
    using scope_type = typename TScope::template scope<TExpected, TGiven>;

   public:
    template <class T>
    using is_referable = typename scope_type::template is_referable<T>;

    template <class T, class TName, class TProvider>
    static auto try_create(const TProvider& provider) -> decltype(scope_type{}.template try_create<T, TName>(provider));

    template <class T, class TName, class TProvider>
    auto create(const TProvider& provider) {
      static std::shared_ptr<TGiven> null{nullptr};
      return get_session() ? scope_.template create<T, TName>(provider) : null;
    }

   private:
    scope_type scope_;
  };

  auto operator()() const { return session_guard{get_session()}; }

 private:
  static auto& get_session() {
    static auto is_in_session = false;
    return is_in_session;
  }
};

template <class TName, class TScope = di::scopes::singleton>
auto session(const TName&, const TScope& = {}) {
  return session_scope<TName, TScope>{};
}

//<-
struct interface1 {
  virtual ~interface1() noexcept = default;
  virtual void dummy() = 0;
};
struct implementation1 : interface1 {
  void dummy() override {}
};
//->

auto my_session = [] {};

int main() {
  {
    // clang-format off
    auto injector = di::make_injector(
      di::bind<interface1>().to<implementation1>().in(session(my_session))
    );
    // clang-format on
    assert(!injector.create<std::shared_ptr<interface1>>());

    {
      auto ms = session(my_session)();
      assert(injector.create<std::shared_ptr<interface1>>());
    }  // end of my_session

    assert(!injector.create<std::shared_ptr<interface1>>());
  }

  {
    // clang-format off
    di::injector<std::shared_ptr<interface1>> injector =
      di::make_injector(
        di::bind<interface1>().to<implementation1>().in(session(my_session))
      );
    // clang-format on
    assert(!injector.create<std::shared_ptr<interface1>>());

    {
      auto ms = session(my_session)();
      assert(injector.create<std::shared_ptr<interface1>>());
    }  // end of my_session

    assert(!injector.create<std::shared_ptr<interface1>>());
  }
}

max_stars_repo_name: robhendriks/city-defence

max_issues_repo_name: robhendriks/city-defence

max_forks_repo_name: robhendriks/city-defence


text:
// Use, modification and distribution are subject to the
// Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)

// Copyright Christopher Kormanyos 2016.

// This file is written to be included from a Quickbook .qbk document.
// It can be compiled by the C++ compiler, and run. Any output can
// also be added here as comment or included or pasted in elsewhere.
// Caution: this file contains Quickbook markup as well as code
// and comments: don't change any of the special comment markups!

// This file also includes Doxygen-style documentation about the function of the code.
// See http://www.doxygen.org for details.

//! \file

//! \brief Example program showing a bare metal real-time performance measurement of a derivative(negatable), 32-bit.

#include <mcal_benchmark.h>
#include <mcal_cpu.h>
#include <mcal_irq.h>
#include <mcal_port.h>

#define BOOST_FIXED_POINT_DISABLE_MULTIPRECISION // Do not use Boost.Multiprecision.
#define BOOST_FIXED_POINT_DISABLE_IOSTREAM       // Do not use I/O streaming.

#include <boost/fixed_point/fixed_point.hpp>

typedef boost::fixed_point::negatable<6, -9> numeric_type;
//typedef float numeric_type;

namespace app
{
  namespace benchmark
  {
    void task_init();
    void task_func();
  }

  typedef mcal::benchmark::benchmark_port_type port_type;
}

namespace local
{
  template<typename RealValueType,
           typename RealFunctionType>
  RealValueType first_derivative(const RealValueType& x,
                                 const RealValueType& dx,
                                 RealFunctionType real_function)
  {
    const RealValueType dx2(dx  + dx);
    const RealValueType dx3(dx2 + dx);

    const RealValueType m1((  real_function(x + dx)
                            - real_function(x - dx))  / 2U);
    const RealValueType m2((  real_function(x + dx2)
                            - real_function(x - dx2)) / 4U);
    const RealValueType m3((  real_function(x + dx3)
                            - real_function(x - dx3)) / 6U);

    const RealValueType fifteen_m1(m1 * 15U);
    const RealValueType six_m2    (m2 *  6U);
    const RealValueType ten_dx    (dx * 10U);

    return ((fifteen_m1 - six_m2) + m3) / ten_dx;
  }
}

numeric_type a;
numeric_type b;
numeric_type c;
numeric_type d;

void app::benchmark::task_init()
{
  port_type::set_direction_output();

  a = numeric_type(12U) / 10U;
  b = numeric_type(34U) / 10U;
  c = numeric_type(56U) / 10U;
}

void app::benchmark::task_func()
{
  // Compute the approximate derivative of (a * x^2) + (b * x) + c
  // evaluated at 1/2, where the approximate values of the coefficients
  // are: a = 1.2, b = 3.4, and c = 5.6. The step-size for evaluating
  // the derivative is set to a value of approximately 1/4.

  mcal::irq::disable_all();
  port_type::set_pin_high();

  d = local::first_derivative(numeric_type(1U) / 2U, // x-value
                              numeric_type(1U) / 4U, // step size dx
                              [](const numeric_type& x) -> numeric_type
                              {
                                return (((a * x) + b) * x) + c;
                              });

  port_type::set_pin_low();
  mcal::irq::enable_all();

  // The expected result is ((2 * a) + b) = (2.4 + 3.4) = 4.6 (exact).
  // We obtain a fixed-point result of approximately 4.5938.

  // Verify that the result lies within (4.5 < result < 4.7).
  // The expected result is 4.6, so this is a wide tolerance.

  const bool value_is_ok =    (d > numeric_type(4))
                           && (d < numeric_type(5));

  if(value_is_ok)
  {
    // The benchmark is OK.
    // Perform one nop and leave.

    mcal::cpu::nop();
  }
  else
  {
    // The benchmark result is not OK!
    // Remain in a blocking loop and crash the system.

    for(;;) { mcal::cpu::nop(); }
  }
}


text:
/*******************************************************************************
 * Copyright (C) 2011 HPCC Systems.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 ******************************************************************************/

#include <boost/algorithm/string.hpp>

#include "HPCCSystemsGraphViewControlAPI.h"

#include "HPCCSystemsGraphViewControl.h"
#if defined FB_WIN
#include "PluginWindowWin.h"
#elif defined FB_X11
#include "PluginWindowX11.h"
#elif defined FB_MACOSX
#include "PluginWindowMac.h"
#endif

#include <XgmmlParser.h>
#include <DotParser.h>

const char * const emptyStr = "";

///////////////////////////////////////////////////////////////////////////////
/// @fn HPCCSystemsGraphViewControl::StaticInitialize()
///
/// @brief  Called from PluginFactory::globalPluginInitialize()
///
/// @see FB::FactoryBase::globalPluginInitialize
///////////////////////////////////////////////////////////////////////////////
void HPCCSystemsGraphViewControl::StaticInitialize()
{
    // Place one-time initialization stuff here; As of FireBreath 1.4 this should only
    // be called once per process
}

///////////////////////////////////////////////////////////////////////////////
/// @fn HPCCSystemsGraphViewControl::StaticInitialize()
///
/// @brief  Called from PluginFactory::globalPluginDeinitialize()
///
/// @see FB::FactoryBase::globalPluginDeinitialize
///////////////////////////////////////////////////////////////////////////////
void HPCCSystemsGraphViewControl::StaticDeinitialize()
{
    // Place one-time deinitialization stuff here. As of FireBreath 1.4 this should
    // always be called just before the plugin library is unloaded
}

///////////////////////////////////////////////////////////////////////////////
/// @brief  HPCCSystemsGraphViewControl constructor.  Note that your API is not available
///         at this point, nor the window.  For best results wait to use
///         the JSAPI object until the onPluginReady method is called
///////////////////////////////////////////////////////////////////////////////
HPCCSystemsGraphViewControl::HPCCSystemsGraphViewControl()
{
	m_g = hpcc::CreateGraph();

	m_buffer = hpcc::CreateGraphBuffer(0, 0);
	m_hotItem = hpcc::CreateGraphHotItem();
	m_selection = hpcc::CreateGraphSelectionBag();

	m_gr = hpcc::CreateGraphRender(m_g, m_buffer, m_hotItem, m_selection);
}

///////////////////////////////////////////////////////////////////////////////
/// @brief  HPCCSystemsGraphViewControl destructor.
///////////////////////////////////////////////////////////////////////////////
HPCCSystemsGraphViewControl::~HPCCSystemsGraphViewControl()
{
    // This is optional, but if you reset m_api (the shared_ptr to your JSAPI
    // root object) and tell the host to free the retained JSAPI objects then
    // unless you are holding another shared_ptr reference to your JSAPI object
    // they will be released here.
    releaseRootJSAPI();
    m_host->freeRetainedObjects();
}

void HPCCSystemsGraphViewControl::onPluginReady()
{
    // When this is called, the BrowserHost is attached, the JSAPI object is
    // created, and we are ready to interact with the page and such.  The
    // PluginWindow may or may not have already fire the AttachedEvent at
    // this point.
}

void HPCCSystemsGraphViewControl::shutdown()
{
    // This will be called when it is time for the plugin to shut down;
    // any threads or anything else that may hold a shared_ptr to this
    // object should be released here so that this object can be safely
    // destroyed. This is the last point that shared_from_this and weak_ptr
    // references to this object will be valid
}

///////////////////////////////////////////////////////////////////////////////
/// @brief  Creates an instance of the JSAPI object that provides your main
///         Javascript interface.
///
/// Note that m_host is your BrowserHost and shared_ptr returns a
/// FB::PluginCorePtr, which can be used to provide a
/// boost::weak_ptr<HPCCSystemsGraphViewControl> for your JSAPI class.
///
/// Be very careful where you hold a shared_ptr to your plugin class from,
/// as it could prevent your plugin class from getting destroyed properly.
///////////////////////////////////////////////////////////////////////////////
FB::JSAPIPtr HPCCSystemsGraphViewControl::createJSAPI()
{
    // m_host is the BrowserHost
    return boost::make_shared<HPCCSystemsGraphViewControlAPI>(FB::ptr_cast<HPCCSystemsGraphViewControl>(shared_from_this()), m_host);
}

void HPCCSystemsGraphViewControl::Invalidate()
{
	FB::PluginWindow* window = GetWindow();
	if (window)
	{
		window->InvalidateWindow();
	}
}

void HPCCSystemsGraphViewControl::InvalidateWorldRect(const hpcc::RectD & worldRect)
{
	Invalidate();
}

int HPCCSystemsGraphViewControl::GetScrollOffsetX()
{
	return static_cast<int>(m_ptOffset.x);
}

int HPCCSystemsGraphViewControl::GetScrollOffsetY()
{
	return static_cast<int>(m_ptOffset.y);
}

void HPCCSystemsGraphViewControl::SetScrollOffset(int x, int y)
{
    m_ptOffset.x = x < 0 ? 0 : (x > m_ptSize.x ? m_ptSize.x : x);
    m_ptOffset.y = y < 0 ? 0 : (y > m_ptSize.y ? m_ptSize.y : y);
    Invalidate();
}

void HPCCSystemsGraphViewControl::SetScrollSize(int w, int h, bool redraw)
{
	FB::PluginWindow* window = GetWindow();
	if (window)
	{
		int width = GetWindow()->getWindowWidth();
		int height = GetWindow()->getWindowHeight();

		m_ptSize.x = w - width;
		m_ptSize.y = h - height;
	}
}

bool HPCCSystemsGraphViewControl::GetClientRectangle(hpcc::RectD & rect)
{
	FB::PluginWindow* window = GetWindow();
	if (window)
	{
		FB::Rect rect2 = GetWindow()->getWindowPosition();
		rect.x = rect2.left;
		rect.y = rect2.top;
		rect.Width = rect2.right - rect2.left;
		rect.Height = rect2.bottom - rect2.top;
		return true;
	}
	return false;
}

void HPCCSystemsGraphViewControl::InvalidateSelection()
{
	Invalidate();
}

//  This is the only method not on main thread  ---
void HPCCSystemsGraphViewControl::operator()(const std::string & dot, const std::string & svg)
{
	{
		boost::recursive_mutex::scoped_lock lock(m_pendingMutex);
		m_pendingDot = dot;
		m_pendingSvg = svg;
	}
	Invalidate();
#if defined FB_WIN
	::UpdateWindow(((FB::PluginWindowWin*)GetWindow())->getHWND());
#endif
}

void HPCCSystemsGraphViewControl::CalcScrollbars(bool redraw)
{
	hpcc::RectD graphRect = m_gr->GetBoundingRect();

	hpcc::RectD clientRect;
	GetClientRectangle(clientRect);

	unsigned int scrollWidth = (unsigned int)(graphRect.Width + clientRect.Width);
	unsigned int scrollHeight = (unsigned int)(graphRect.Height + clientRect.Height);

	hpcc::PointD offset;
	offset.x = clientRect.Width / 2.0f;
	offset.y = clientRect.Height / 2.0f;

	m_gr->SetOffset(offset);

	SetScrollSize(scrollWidth, scrollHeight, redraw);
}
void HPCCSystemsGraphViewControl::CenterOn(const hpcc::PointD & worldPoint)
{
	hpcc::PointD newCenter = m_gr->WorldToScreen(worldPoint);

	hpcc::RectD clientRect;
	GetClientRectangle(clientRect);

	SetScrollOffset((int)(newCenter.x - clientRect.Width / 2.0f), (int)(newCenter.y - clientRect.Height / 2.0f));
}

hpcc::PointD HPCCSystemsGraphViewControl::GetCenterPoint(bool world)
{
	hpcc::RectD rc;
	GetClientRectangle(rc);
	rc.ResetOrigin();
	hpcc::PointD point;
	rc.GetCenter(&point);
	point.x += GetScrollOffsetX();
	point.y += GetScrollOffsetY();

	if (world)
		return m_gr->ScreenToWorld(point);
	return point;
}

void HPCCSystemsGraphViewControl::MoveTo(const hpcc::PointD & worldPoint, int x, int y)
{
	hpcc::PointD newCenter = m_gr->WorldToScreen(worldPoint);

	SetScrollOffset((int)(newCenter.x - x), (int)(newCenter.y - y));
}

void HPCCSystemsGraphViewControl::Clear()
{
	std::string layout = m_g->GetPropertyString(hpcc::PROP_LAYOUT);
	m_hotItem->Set(NULL);
	m_selection->Clear();
	m_g->Clear();
	Invalidate();
}

void HPCCSystemsGraphViewControl::LoadTestData()
{
	m_g->SetProperty(hpcc::DOT_LABEL, "Graph 1");
	//::LoadTestData(m_g, NULL, NULL, 0);
}

void HPCCSystemsGraphViewControl::LoadXML(const std::string & verticesXML, const std::string & edgesXML)
{
	std::string layout = m_g->GetPropertyString(hpcc::PROP_LAYOUT);
	m_hotItem->Set(NULL);
	m_selection->Clear();
	m_g->Clear();
	hpcc::LoadXML(m_g, verticesXML, edgesXML);
}

void HPCCSystemsGraphViewControl::LoadXML2(const std::string & xml)
{
	std::string layout = m_g->GetPropertyString(hpcc::PROP_LAYOUT);
	m_hotItem->Set(NULL);
	m_selection->Clear();
	m_g->Clear();
	hpcc::LoadXML2(m_g, xml);
}

void HPCCSystemsGraphViewControl::LoadXGMML(const std::string & xgmml)
{
	std::string layout = m_g->GetPropertyString(hpcc::PROP_LAYOUT);
	m_hotItem->Set(NULL);
	m_selection->Clear();
	m_g->Clear();
	hpcc::LoadXGMML(m_g, xgmml);
}

void HPCCSystemsGraphViewControl::MergeXGMML(const std::string & xgmml)
{
	std::string layout = m_g->GetPropertyString(hpcc::PROP_LAYOUT);
	hpcc::MergeXGMML(m_g, xgmml);
	Invalidate();
}

bool HPCCSystemsGraphViewControl::MergeSVG(const std::string & svg)
{
	if (hpcc::MergeSVG(m_g, svg))
	{
		m_svg = svg;
		CalcScrollbars();
		CenterOnGraphItem(NULL);
		Invalidate();
		return true;
	}
	return false;
}

void HPCCSystemsGraphViewControl::LoadDOT(const std::string & dot)
{
	std::string layout = m_g->GetPropertyString(hpcc::PROP_LAYOUT);
	m_hotItem->Set(NULL);
	m_selection->Clear();
	m_g->Clear();
	hpcc::LoadDOT(m_g, dot);
}

const std::string HPCCSystemsGraphViewControl::GetSVG()
{
	return m_svg;
}

const std::string HPCCSystemsGraphViewControl::GetDOT()
{
	return m_dot;
}

const char * HPCCSystemsGraphViewControl::GetLocalisedXGMML(const std::vector<int> & items, int localisationDepth, int localisationDistance, std::string & xgmml)
{
	hpcc::IGraphItemSet graphItems;
	for(std::vector<int>::const_iterator itr = items.begin(); itr != items.end(); ++itr)
	{
		graphItems.insert(m_g->GetGraphItem(*itr));
	}
	hpcc::WriteLocalisedXGMML(m_g, graphItems, localisationDepth, localisationDistance, xgmml);
	return xgmml.c_str();
}

double HPCCSystemsGraphViewControl::SetScale(double scale)
{
	//  Keep center of screen in the middle
	hpcc::PointD worldDblClk = GetCenterPoint(true);

	double oldScale = m_gr->SetScale(scale);
	if (oldScale != scale)
	{
		CalcScrollbars();
		CenterOn(worldDblClk);
	}
	Invalidate();
	return oldScale;
}

double HPCCSystemsGraphViewControl::GetScale()
{
	return m_gr->GetScale();
}

void HPCCSystemsGraphViewControl::CenterOnGraphItem(hpcc::IGraphItem * item)
{
	hpcc::RectD itemRect = m_gr->GetBoundingRect(item, true);
	hpcc::PointD itemCP;
	itemRect.GetCenter(&itemCP);
	CenterOn(itemCP);
}

void HPCCSystemsGraphViewControl::CenterOnItem(int _item, bool sizeToFit, bool widthOnly)
{
	hpcc::IGraphItemPtr item = m_g->GetGraphItem(_item);
	bool scaleChanged = false;
	if (sizeToFit)
	{
		hpcc::RectD rc;
		GetClientRectangle(rc);

		double oldScale = m_gr->ScaleToFit(item, rc, widthOnly);
		if (oldScale != m_gr->GetScale())
			scaleChanged = true;
	}

	if (scaleChanged)
	{
		CalcScrollbars();
		CenterOnGraphItem(item);
		boost::static_pointer_cast<HPCCSystemsGraphViewControlAPI>(getRootJSAPI())->fire_Scaled((int)(m_gr->GetScale() * 100));
	}
	else
		CenterOnGraphItem(item);
}

void HPCCSystemsGraphViewControl::StartLayout(const std::string & layout)
{
	m_g->SetProperty(hpcc::PROP_LAYOUT, layout.c_str());
	hpcc::DoGraphvizLayout(m_g, m_dot, boost::ref(*this));
}

void HPCCSystemsGraphViewControl::SetMessage(const std::wstring & msg)
{
	m_gr->SetMessage(msg);
	Invalidate();
}

int HPCCSystemsGraphViewControl::Find(const std::string & text, bool includeProperties, std::vector<int> & results)
{
	const hpcc::IVertexSet & vs = m_g->GetAllVertices();
	for(hpcc::IVertexSet::const_iterator itr = vs.begin(); itr != vs.end(); ++itr)
	{
		if (boost::algorithm::icontains(itr->get()->GetPropertyString(hpcc::DOT_LABEL), text))
			results.push_back(itr->get()->GetID());
	}
	return results.size();
}

bool HPCCSystemsGraphViewControl::HasItems()
{
	return !m_g->GetAllVertices().empty() || !m_g->GetAllEdges().empty() || !m_g->GetClusters().empty();
}

int HPCCSystemsGraphViewControl::GetSelection(std::vector<int> & results)
{
	return m_selection->GetSelection(results);
}

int HPCCSystemsGraphViewControl::GetSelection(std::vector<std::string> & results)
{
	return m_selection->GetSelection(m_g, results);
}

bool HPCCSystemsGraphViewControl::SetSelected(const std::vector<int> & _items, bool clearPrevious)
{
	hpcc::IGraphItemSet items;
	for(std::vector<int>::const_iterator itr = _items.begin(); itr != _items.end(); ++itr)
	{
		hpcc::IGraphItemPtr item = m_g->GetGraphItem(*itr);
		if (item)
			items.insert(item);
	}
	bool changed = m_selection->Select(items, clearPrevious);
	if (changed)
		InvalidateSelection();
	return changed;
}

bool HPCCSystemsGraphViewControl::SetSelected(const std::vector<std::string> & _items, bool clearPrevious)
{
	hpcc::IGraphItemSet items;
	for(std::vector<std::string>::const_iterator itr = _items.begin(); itr != _items.end(); ++itr)
	{
		hpcc::IGraphItemPtr item = m_g->GetGraphItem(GetItem(*itr));
		if (item)
			items.insert(item);
	}
	bool changed = m_selection->Select(items, clearPrevious);
	if (changed)
		InvalidateSelection();
	return changed;
}

const hpcc::IClusterSet & HPCCSystemsGraphViewControl::GetClusters()
{
	return m_g->GetClusters();
}

int HPCCSystemsGraphViewControl::GetProperties(int _item, hpcc::ciStringStringMap & results)
{
	hpcc::IGraphItemPtr item = m_g->GetGraphItem(_item);
	if (item)
		item->GetProperties(results);
	return results.size();
}

void HPCCSystemsGraphViewControl::SetProperty(int _item, const std::string & key, const std::string & value)
{
	hpcc::IGraphItemPtr item = m_g->GetGraphItem(_item);
	if (item)
		item->SetProperty(key, value);
}

const char * HPCCSystemsGraphViewControl::GetProperty(int _item, const std::string & key)
{
	hpcc::IGraphItemPtr item = m_g->GetGraphItem(_item);
	if (item && item->HasProperty(key))
		return item->GetProperty(key);
	return emptyStr;
}

unsigned int HPCCSystemsGraphViewControl::GetParent(int _item)
{
	hpcc::IGraphItem * item = m_g->GetGraphItem(_item);
	if (item)
	{
		hpcc::IGraphItem * parent = NULL;
		if (hpcc::ICluster * cluster = dynamic_cast<hpcc::ICluster *>(item)) {
			parent = cluster->GetParent();
		} else if (hpcc::IVertex * vertex = dynamic_cast<hpcc::IVertex *>(item)) {
			parent = vertex->GetParent();
		}
		if (parent)
		{
			return parent->GetID();
		}
	}
	return -1;
}

int HPCCSystemsGraphViewControl::GetChildren(int _item, std::vector<int> & results)
{
	hpcc::IGraphItem * item = m_g->GetGraphItem(_item);
	if (item)
	{
		if (hpcc::ICluster * cluster = dynamic_cast<hpcc::ICluster *>(item)) {
			const hpcc::IClusterSet & cluster_children = cluster->GetClusters();
			for(hpcc::IClusterSet::const_iterator itr = cluster_children.begin(); itr != cluster_children.end(); ++itr)
				results.push_back(itr->get()->GetID());

			const hpcc::IVertexSet & vertex_children = cluster->GetVertices();
			for(hpcc::IVertexSet::const_iterator itr = vertex_children.begin(); itr != vertex_children.end(); ++itr)
				results.push_back(itr->get()->GetID());
		}
	}
	return results.size();
}

int HPCCSystemsGraphViewControl::GetInEdges(int _item, std::vector<int> & results)
{
	hpcc::IGraphItem * item = m_g->GetGraphItem(_item);
	if (item)
	{
		if (hpcc::IVertex * vertex = dynamic_cast<hpcc::IVertex *>(item)) {
			const hpcc::IEdgeSet & edges = vertex->GetInEdges();
			for(hpcc::IEdgeSet::const_iterator itr = edges.begin(); itr != edges.end(); ++itr)
				results.push_back(itr->get()->GetID());
		}
	}
	return results.size();
}

int HPCCSystemsGraphViewControl::GetOutEdges(int _item, std::vector<int> & results)
{
	hpcc::IGraphItem * item = m_g->GetGraphItem(_item);
	if (item)
	{
		if (hpcc::IVertex * vertex = dynamic_cast<hpcc::IVertex *>(item)) {
			const hpcc::IEdgeSet & edges = vertex->GetOutEdges();
			for(hpcc::IEdgeSet::const_iterator itr = edges.begin(); itr != edges.end(); ++itr)
				results.push_back(itr->get()->GetID());
		}
	}
	return results.size();
}

unsigned int HPCCSystemsGraphViewControl::GetSource(int _item)
{
	hpcc::IGraphItem * item = m_g->GetGraphItem(_item);
	if (item)
	{
		if (hpcc::IEdge * edge = dynamic_cast<hpcc::IEdge *>(item)) {
			return edge->GetFromVertex()->GetID();
		}
	}
	return -1;
}

unsigned int HPCCSystemsGraphViewControl::GetTarget(int _item)
{
	hpcc::IGraphItem * item = m_g->GetGraphItem(_item);
	if (item)
	{
		if (hpcc::IEdge * edge = dynamic_cast<hpcc::IEdge *>(item)) {
			return edge->GetToVertex()->GetID();
		}
	}
	return -1;
}

unsigned int HPCCSystemsGraphViewControl::GetItem(const std::string &externalID)
{
	if (externalID.compare("0") == 0)
		return m_g->GetID();
	if (hpcc::IGraphItem * item = m_g->GetVertex(externalID, true))
		return item->GetID();
	if (hpcc::IGraphItem * item = m_g->GetEdge(externalID, true))
		return item->GetID();
	if (hpcc::IGraphItem * item = m_g->GetCluster(externalID, true))
		return item->GetID();
	return -1;
}

const char * HPCCSystemsGraphViewControl::GetGlobalType(int item)
{
	return m_g->GetExternalType(item);
}

const char * HPCCSystemsGraphViewControl::GetGlobalID(int item)
{
	return m_g->GetExternalID(item);
}

int HPCCSystemsGraphViewControl::GetClusters(std::vector<int> & results)
{
	const hpcc::IClusterSet & clusters = m_g->GetAllClusters();
	for(hpcc::IClusterSet::const_iterator itr = clusters.begin(); itr != clusters.end(); ++itr)
		results.push_back(itr->get()->GetID());
	return results.size();
}

int HPCCSystemsGraphViewControl::GetVertices(std::vector<int> & results)
{
	const hpcc::IVertexSet & vertices = m_g->GetAllVertices();
	for(hpcc::IVertexSet::const_iterator itr = vertices.begin(); itr != vertices.end(); ++itr)
		results.push_back(itr->get()->GetID());
	return results.size();
}

int HPCCSystemsGraphViewControl::GetEdges(std::vector<int> & results)
{
	const hpcc::IEdgeSet & edges = m_g->GetAllEdges();
	for(hpcc::IEdgeSet::const_iterator itr = edges.begin(); itr != edges.end(); ++itr)
		results.push_back(itr->get()->GetID());
	return results.size();
}

bool HPCCSystemsGraphViewControl::onMouseDown(FB::MouseDownEvent *evt, FB::PluginWindow *)
{
	m_mouseDown = MOUSEDOWN_NORMAL;
	m_mouseDownPosX = evt->m_x;
	m_mouseDownPosY = evt->m_y;
	m_scrollDownPosX = GetScrollOffsetX();
	m_scrollDownPosY = GetScrollOffsetY();
    return true;
}

bool HPCCSystemsGraphViewControl::onMouseDoubleClick(FB::MouseDoubleClickEvent *evt, FB::PluginWindow *)
{
	hpcc::PointD point(evt->m_x, evt->m_y);
	m_mouseDown = MOUSEDOWN_DBLCLK;
	point.Offset(m_ptOffset);
	hpcc::PointD worldDblClk(point.x, point.y);
	worldDblClk = m_gr->ScreenToWorld(worldDblClk);

	hpcc::IGraphItem * item = m_gr->GetItemAt(worldDblClk.x, worldDblClk.y, true);
	boost::static_pointer_cast<HPCCSystemsGraphViewControlAPI>(getRootJSAPI())->fire_MouseDoubleClick(item ? item->GetID() : 0);
	return true;
}

bool HPCCSystemsGraphViewControl::onMouseUp(FB::MouseUpEvent *evt, FB::PluginWindow *)
{
	hpcc::PointD point(evt->m_x, evt->m_y);
	switch (m_mouseDown)
	{
	case MOUSEDOWN_DBLCLK:
	case MOUSEDOWN_MOVED:
		m_mouseDown = MOUSEDOWN_UNKNOWN;
		return true;
	}

	m_mouseDown = MOUSEDOWN_UNKNOWN;
	point.Offset(m_ptOffset);
	hpcc::IGraphItem * selectedItem = m_gr->GetItemAt(point.x, point.y);

	bool selChanged = false;
	if (selectedItem)
	{
		if (evt->m_state & FB::MouseButtonEvent::ModifierState_Control)
		{
			if (m_selection->IsSelected(selectedItem))
				selChanged = m_selection->Deselect(selectedItem);
			else
				selChanged = m_selection->Select(selectedItem, false);
		}
		else
			selChanged = m_selection->Select(selectedItem);
	}

	if (selChanged)
	{
		InvalidateSelection();

		std::vector<int> selection;
		m_selection->GetSelection(selection);

		FB::VariantList items;
		for(std::vector<int>::const_iterator itr = selection.begin(); itr != selection.end(); ++itr)
			items.push_back(*itr);

		boost::static_pointer_cast<HPCCSystemsGraphViewControlAPI>(getRootJSAPI())->fire_SelectionChanged(items);
	}
    return true;
}

bool HPCCSystemsGraphViewControl::onMouseMove(FB::MouseMoveEvent *evt, FB::PluginWindow *)
{
	hpcc::PointD point(evt->m_x, evt->m_y);
	switch (m_mouseDown)
	{
	case MOUSEDOWN_NORMAL:
	case MOUSEDOWN_MOVED:
		{
			int deltaX = static_cast<int>(point.x - m_mouseDownPosX);
			int deltaY = static_cast<int>(point.y - m_mouseDownPosY);

			SetScrollOffset(m_scrollDownPosX - deltaX, m_scrollDownPosY - deltaY);
			if (deltaX || deltaY)
				m_mouseDown = MOUSEDOWN_MOVED;
		}
		break;
	default:
		{
			point.Offset(m_ptOffset);
			hpcc::IGraphItem * hotItem = NULL;
			if (!hotItem)
				hotItem = m_gr->GetItemAt(point.x, point.y);
			if (m_hotItem->Set(hotItem))
			{
				hpcc::RectD invalidateRect;
				if (m_hotItem->GetInvalidateRect(invalidateRect))
					InvalidateWorldRect(invalidateRect);
				if (m_hotItem->GetPrevInvalidateRect(invalidateRect))
					InvalidateWorldRect(invalidateRect);
			}
		}
		break;
	}
	return true;
}

bool HPCCSystemsGraphViewControl::onMouseScroll(FB::MouseScrollEvent *evt, FB::PluginWindow *)
{
	hpcc::PointD pt(evt->m_x, evt->m_y);
	hpcc::PointD delta(evt->m_dx, evt->m_dy);
	if (evt->m_state & FB::MouseButtonEvent::ModifierState_Control)
	{
		hpcc::PointD point = pt;
		point.Offset(m_ptOffset);
		hpcc::PointD worldDblClk(point.x, point.y);
		worldDblClk = m_gr->ScreenToWorld(worldDblClk);
		double zDelta = delta.x != 0 ? delta.x : delta.y;
		double scale = m_gr->GetScale();
		if (zDelta > 0)
			m_gr->SetScale(scale + 0.05 * scale);
		else
			m_gr->SetScale(scale - 0.05 * scale);

		CalcScrollbars();
		MoveTo(worldDblClk, static_cast<int>(pt.x), static_cast<int>(pt.y));

		boost::static_pointer_cast<HPCCSystemsGraphViewControlAPI>(getRootJSAPI())->fire_Scaled((int)(m_gr->GetScale() * 100));
	} else {
		hpcc::PointD point = pt;
		point.Offset(m_ptOffset);
		hpcc::PointD worldDblClk(point.x, point.y);
		worldDblClk = m_gr->ScreenToWorld(worldDblClk);

		pt.x += delta.x;
		pt.y += delta.y;

		MoveTo(worldDblClk, static_cast<int>(pt.x), static_cast<int>(pt.y));
	}
    return true;
}

void HPCCSystemsGraphViewControl::FinishLayout()
{
	{
		boost::recursive_mutex::scoped_lock lock(m_pendingMutex);
		if (m_pendingDot.empty() || !boost::algorithm::equals(m_dot, m_pendingDot))
		{
			m_pendingDot = "";
			m_pendingSvg = "";
			return;
		}
		MergeSVG(m_pendingSvg);
		m_pendingDot = "";
		m_pendingSvg = "";
	}

	boost::static_pointer_cast<HPCCSystemsGraphViewControlAPI>(getRootJSAPI())->fire_LayoutFinished();
}

bool HPCCSystemsGraphViewControl::onResized(FB::ResizedEvent *evt, FB::PluginWindow *)
{
	CalcScrollbars();
	return true;
}

bool HPCCSystemsGraphViewControl::onRefresh(FB::RefreshEvent *evt, FB::PluginWindow *)
{
	FinishLayout();

	hpcc::RectI bounds(0, 0, evt->bounds.right, evt->bounds.bottom);
	hpcc::RectI rect(bounds);
	rect.Offset(static_cast<int>(m_ptOffset.x), static_cast<int>(m_ptOffset.y));

	m_buffer->Resize(rect.Width(), rect.Height());
	m_gr->DoRender(rect, true);

#if defined FB_WIN
	m_buffer->Draw(((FB::PluginWindowWin*)GetWindow())->getHWND(), bounds.left, bounds.top);
#elif defined FB_MACOSX
	m_buffer->Draw(((FB::CoreGraphicsDraw *)evt)->context, bounds.left, bounds.top);
#elif defined FB_X11
	m_buffer->Draw(((FB::PluginWindowX11*)GetWindow())->getWidget(), bounds.left, bounds.top);
#endif

	return true;
}

bool HPCCSystemsGraphViewControl::onWindowAttached(FB::AttachedEvent *evt, FB::PluginWindow *)
{
    // The window is attached; act appropriately
#if defined FB_WIN
	((FB::PluginWindowWin*)GetWindow())->setSuppressEraseBackground(true);
#endif
    return false;
}

bool HPCCSystemsGraphViewControl::onWindowDetached(FB::DetachedEvent *evt, FB::PluginWindow *)
{
    // The window is about to be detached; act appropriately
    return false;
}

max_stars_repo_name: GordonSmith/GraphControl

max_issues_repo_name: GordonSmith/GraphControl

max_forks_repo_name: GordonSmith/GraphControl


text:
/*  _________________________________________________________________________
 *
 *  UTILIB: A utility library for developing portable C++ codes.
 *  Copyright (c) 2008 Sandia Corporation.
 *  This software is distributed under the BSD License.
 *  Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
 *  the U.S. Government retains certain rights in this software.
 *  For more information, see the README file in the top UTILIB directory.
 *  _________________________________________________________________________
 */

#include <utilib/Property.h>

#ifdef UTILIB_HAVE_BOOST
#include <boost/bind.hpp>
#endif

namespace utilib {

//---------------------------------------------------------------------
#ifdef UTILIB_HAVE_BOOST

namespace {

void property_updater(Property* target, const ReadOnly_Property& source)
{
   target->set(source.get());
}
void privileged_updater( Privileged_Property* target,
                         const ReadOnly_Property& source )
{
   target->set(source.get());
}

} // namespace utilib::(local)

Property::bound_set_t
Property::bind_set()
{
   return boost::bind(&utilib::property_updater, this, _1);
}

Property::bound_set_t
Privileged_Property::bind_set()
{
   return boost::bind(&utilib::privileged_updater, this, _1);
}

#endif // UTILIB_HAVE_BOOST
//---------------------------------------------------------------------

namespace {
/** This map is treated as a stack of the currently active onChange
 *  events so we can easily identify and break out of infinite onChange
 *  loops.
 *
 *  NOTE: this implementation is NOT threadsafe.  A threadsafe
 *  implementation would probably look more like:
 *
 *   std::map<thread_id, std::map<Property*, size_t> >
 */
typedef std::map<Property*, size_t>  _s_onchange_stack_t;
_s_onchange_stack_t _s_onchange_stack = std::map<Property*, size_t>();

/** This map allows Privileged Properties to temporarily bypass the
 *  onChange loop detection system in a controlled manner.
 */
std::map< Privileged_Property*,
          std::list<_s_onchange_stack_t> >  _s_onchange_loops;

bool push_onchange_stack(Property* prop)
{
   return _s_onchange_stack.insert
      ( std::make_pair(prop, _s_onchange_stack.size()+1) ).second;
}

void pop_onchange_stack(Property* prop)
{
   std::map<Property*, size_t>::iterator it = _s_onchange_stack.find(prop);
   if ( it == _s_onchange_stack.end() )
      EXCEPTION_MNGR(std::logic_error, "Property: pop_onchange_stack(): "
                     "attempt to pop a property off the stack that is not "
                     "on the stack");

   if ( it->second != _s_onchange_stack.size() )
      EXCEPTION_MNGR(std::logic_error, "Property: pop_onchange_stack(): "
                     "attempt to pop a property off the stack that is not "
                     "on the top of the stack");

   _s_onchange_stack.erase(it);
}

} // namespace utilib::(local)

/** This implements the actual routine for setting a Property value.  The
 *  protected indirection allows us to declare sub-classes that bypass
 *  the "is_writable" flag.
 */
void Property::set_impl(utilib::Any new_value, bool writable)
{
   if ( ! writable )
      EXCEPTION_MNGR(property_error, "Property::set(): attempt to set "
                     "a Property declared read-only.");

   // NOTE: this loop check implementation is NOT thread safe!
   if ( ! push_onchange_stack(this) )
      EXCEPTION_MNGR(property_error, "Property::set(): "
                     "onChange callback loop detected "
                     "(onChange triggers changes which trigger itself).");

   // Remembering code for split onChange / replicate events
   //
   //bool bypass_afterChange = true;
   //if ( _s_cb_replicate_buffer.empty() )
   //   bypass_afterChange = false;
   //if ( _s_cb_replicate_visited.count(this) )
   //   EXCEPTION_MNGR(property_error, "Property::set(): "
   //                  "replicate callback loop detected "
   //                  "(replicate triggers an already set onChange.");
   //_s_cb_replicate_buffer.push_back(this);

   // Because of the loop detection, we MUST implement this entire
   // function within a try-catch block so that this Property is not
   // left on the _s_onchange_stack.
   try {
      // For most cases (i.e. using the default set functor on a bound
      // Property), we should first make sure the data is convertable to
      // the Property's bound type.  This has the nice side benefit
      // that any validate listeners who know how the Property was
      // declared can avoid a call to lexical_cast.
      Any converted_new_value;
      if ( ( ! connected(data->set_functor) ) && data->value.is_immutable() )
      {
         if ( new_value.empty() )
            EXCEPTION_MNGR( property_error, "Property::set(): "
                            "attempt to empty a bound Property" );
         else
            TypeManager()->lexical_cast(new_value, converted_new_value,
                                        data->value.type() );
      }
      else
         converted_new_value = new_value;

      // check if the new value is OK
      if ( ! validate()(*this, converted_new_value) )
         EXCEPTION_MNGR(property_error, "Property::set(): set disallowed "
                        "by validator callback for new value = " << new_value);

      // set the Property's data...
      if ( connected(data->set_functor) )
         data->set_functor(data->value, converted_new_value);
      else
      {
         if ( converted_new_value.empty() )
            data->value.clear();
         else
            TypeManager()->lexical_cast(converted_new_value, data->value);
      }

      // We want to defeat the normal Any shallow copies so that the
      // user can't set the Property value without going through the
      // set() method.
      //
      // NB: We must specially check for the Unbound properties
      Any& tmp = ( data->value.is_type(typeid(UntypedAnyContainer))
                   ? data->value.expose<UntypedAnyContainer>().m_data
                   : data->value );
      if ( tmp.references_same_data_as(new_value) )
      {
         Any clone = tmp.clone();
         tmp.clear();
         tmp = clone;
      }

      onChange()(*this);

   } catch ( ... ) {
      pop_onchange_stack(this);
      throw;
   }

   pop_onchange_stack(this);

   // Remembering code for split onChange / replicate events
   //
   //if ( bypass_afterChange )
   //   return;
   //
   //std::set<Property*> visited;
   //while ( ! _s_cb_replicate_buffer.empty() )
   //{
   //   Property* prop = _s_cb_replicate_buffer.front();
   //   if ( _s_cb_replicate_visited.insert(prop).second )
   //      prop->replicate()(*prop);
   //   _s_cb_replicate_buffer.pop_front();
   //}
}

/** Special case: casting a Property to "void" should do nothing.
 */
template<>
void ReadOnly_Property::as<void>() const
{}

/** Special case: casting a Property to "Any" should return the
 *  contained value, and not embed the Property within an Any.
 */
template<>
utilib::Any ReadOnly_Property::as<utilib::Any>() const
{ return get(); }

/** Special case: casting a Property to "AnyRef" should return a
 *  reference to the Property within an AnyRef
 */
template<>
utilib::AnyRef ReadOnly_Property::as<utilib::AnyRef>() const
{
   return asAnyRef();
}

/** Special case: casting a Property to "AnyFixedRef" should return an
 *  immutable reference to the Property within an AnyFixedRef
 */
template<>
utilib::AnyFixedRef ReadOnly_Property::as<utilib::AnyFixedRef>() const
{
   return asAnyFixedRef();
}

/** Special case: when constructing one Property from another with the
 *  following syntax:
 *    \code
 *    Property a;
 *    Property b(a);
 *    \endcode
 *
 *  Some compilers (i.e. GCC 4.1.2) will pick up this template, EVEN if
 *  the explicit copy constructor exists (and in some cases, when it
 *  does, the compiler will die on internal segmentation fault).
 *  Explicitly overloading this template instantiation to "do the right
 *  thing" appears to be the best solution.
 */
template<>
Property::
Property(Property& rhs)
   : ReadOnly_Property(rhs)
{}

template<>
Property::
Property(Privileged_Property& rhs)
   : ReadOnly_Property(rhs)
{}

template<>
Privileged_Property::
Privileged_Property(Privileged_Property& rhs)
   : Property(rhs)
{}

template<>
Property::
Property(const Privileged_Property& rhs)
   : ReadOnly_Property(rhs)
{}

template<>
bool ReadOnly_Property::operator==(const ReadOnly_Property& rhs) const
{ return equality_compare_property(rhs); }

template<>
bool ReadOnly_Property::operator==(const Property& rhs) const
{ return equality_compare_property(rhs); }

template<>
bool ReadOnly_Property::operator==(const Privileged_Property& rhs) const
{ return equality_compare_property(rhs); }

template<>
bool ReadOnly_Property::operator==(const Any& rhs) const
{ return equality_compare_any(rhs); }

template<>
bool ReadOnly_Property::operator==(const AnyRef& rhs) const
{ return equality_compare_any(rhs); }

template<>
bool ReadOnly_Property::operator==(const AnyFixedRef& rhs) const
{ return equality_compare_any(rhs); }

template<>
bool ReadOnly_Property::operator<(const ReadOnly_Property& rhs) const
{ return lessThan_compare_property(rhs); }

template<>
bool ReadOnly_Property::operator<(const Property& rhs) const
{ return lessThan_compare_property(rhs); }

template<>
bool ReadOnly_Property::operator<(const Privileged_Property& rhs) const
{ return lessThan_compare_property(rhs); }

template<>
bool ReadOnly_Property::operator<(const Any& rhs) const
{ return lessThan_compare_any(rhs); }

template<>
bool ReadOnly_Property::operator<(const AnyRef& rhs) const
{ return lessThan_compare_any(rhs); }

template<>
bool ReadOnly_Property::operator<(const AnyFixedRef& rhs) const
{ return lessThan_compare_any(rhs); }

bool
ReadOnly_Property::
equality_compare_property(const ReadOnly_Property& rhs) const
{
   return equality_compare_any(rhs.get());
}

bool
ReadOnly_Property::
equality_compare_any(const Any& rhs) const
{
   if ( rhs.is_type(typeid(ReadOnly_Property)) )
      return equality_compare_property( rhs.expose<ReadOnly_Property>() );
   else if ( rhs.is_type(typeid(Property)) )
      return equality_compare_property( rhs.expose<Property>() );
   else if ( rhs.is_type(typeid(Privileged_Property)) )
      return equality_compare_property( rhs.expose<Privileged_Property>() );

   Any L = get();
   if ( L.empty() || rhs.empty() )
      return L.empty() == rhs.empty();

   utilib::Any tmp;
   try {
      TypeManager()->lexical_cast(L, tmp, rhs.type());
      return rhs == tmp;
   } catch ( utilib::bad_lexical_cast &e ) {
      // silently fall through to casting the rhs to match this property
   } catch ( utilib::any_not_comparable &e ) {
      // silently fall through to casting the rhs to match this property
   }

   TypeManager()->lexical_cast(rhs, tmp, L.type());
   return tmp == L;
}

bool
ReadOnly_Property::
lessThan_compare_property(const ReadOnly_Property& rhs) const
{
   return lessThan_compare_any(rhs.get());
}

bool
ReadOnly_Property::
lessThan_compare_any(const Any& rhs) const
{
   if ( rhs.is_type(typeid(ReadOnly_Property)) )
      return lessThan_compare_property( rhs.expose<ReadOnly_Property>() );
   else if ( rhs.is_type(typeid(Property)) )
      return lessThan_compare_property( rhs.expose<Property>() );
   else if ( rhs.is_type(typeid(Privileged_Property)) )
      return lessThan_compare_property( rhs.expose<Privileged_Property>() );

   Any L = get();
   if ( L.empty() )
      return ! rhs.empty();
   if ( rhs.empty() )
      return false;

   utilib::Any tmp;
   try {
      TypeManager()->lexical_cast(L, tmp, rhs.type());
      return tmp < rhs;
   } catch ( utilib::bad_lexical_cast &e ) {
      // silently fall through to casting the rhs to match this property
   } catch ( utilib::any_not_comparable &e ) {
      // silently fall through to casting the rhs to match this property
   }

   TypeManager()->lexical_cast(rhs, tmp, L.type());
   return L < tmp;
}

Privileged_Property::onChangeLoop_t
Privileged_Property::allowOnChangeRecursion(size_t max)
{
   if ( _s_onchange_loops[this].size() >= max )
      EXCEPTION_MNGR(property_error, "Privileged_Property::"
                     "allowOnChangeRecursion: "
                     "exceeded maximum number of loops (" <<
                     _s_onchange_loops[this].size() << " >= " << max << ")");

   _s_onchange_loops[this].push_back(_s_onchange_stack);
   _s_onchange_stack.clear();
   onChangeLoop_t ans;
   ans->configure(this, _s_onchange_loops[this].size());
   return ans;
}

Privileged_Property::onChangeLoopData::~onChangeLoopData()
{
   if ( ! _s_onchange_stack.empty() )
      EXCEPTION_MNGR(property_error, "Privileged_Property::onChangeLoopData: "
                     "declared onChange loop did not completely unroll.");

   if ( _s_onchange_loops[owner].size() != id )
      EXCEPTION_MNGR(property_error, "Privileged_Property::onChangeLoopData: "
                     "declared onChange loops are unrolling out of order.");

   _s_onchange_stack = _s_onchange_loops[owner].back();
   _s_onchange_loops[owner].pop_back();
}

} // namespace utilib

/** Printing the ReadOnlyProperty prints the contained Any
 *
 *  NB: By declaring a streaming operator for the ReadOnlyProperty
 *  class, all Properties are streamable.
 */
std::ostream& operator<<( std::ostream& os,
                          const utilib::ReadOnly_Property& property )
{
   return os << property.get();
}

/** Reading from an istream reads into the contained Any
 */
std::istream& operator>>( std::istream& is, utilib::Property& property )
{
   // NB: read the property first so that we "know" what data type we
   // should attempt to read into.
   utilib::Any tmp = property.get();
   is >> tmp;
   property = tmp;
   return is;
}

max_stars_repo_name: USEPA/Water-Security-Toolkit

max_issues_repo_name: USEPA/Water-Security-Toolkit

max_forks_repo_name: USEPA/Water-Security-Toolkit