<commit_before>/*
Copyright (c) 2016 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.

Authors: Gabriel Ebner, Leonardo de Moura, Sebastian Ullrich
*/
#if defined(LEAN_JSON)
#include <algorithm>
#include <string>
#include <vector>
#include "util/lean_path.h"
#include "util/sexpr/option_declarations.h"
#include "util/bitap_fuzzy_search.h"
#include "library/protected.h"
#include "library/util.h"
#include "library/scoped_ext.h"
#include "library/class.h"
#include "frontends/lean/util.h"
#include "shell/server.h"
#include "shell/completion.h"

namespace lean {

#define LEAN_FUZZY_MAX_ERRORS          3
#define LEAN_FUZZY_MAX_ERRORS_FACTOR   3

/** \brief Return an (atomic) name if \c n can be referenced by this atomic
    name in the given environment. */
optional<name> is_essentially_atomic(environment const & env, name const & n) {
    if (n.is_atomic())
        return optional<name>(n);
    list<name> const & ns_list = get_namespaces(env);
    for (name const & ns : ns_list) {
        if (is_prefix_of(ns, n)) {
            auto n_prime = n.replace_prefix(ns, name());
            if (n_prime.is_atomic() && !is_protected(env, n))
                return optional<name>(n_prime);
            break;
        }
    }
    if (auto it = is_uniquely_aliased(env, n))
        if (it->is_atomic())
            return it;
    return optional<name>();
}

unsigned get_fuzzy_match_max_errors(unsigned prefix_sz) {
    unsigned r = (prefix_sz / LEAN_FUZZY_MAX_ERRORS_FACTOR);
    if (r > LEAN_FUZZY_MAX_ERRORS)
        return LEAN_FUZZY_MAX_ERRORS;
    return r;
}

optional<name> exact_prefix_match(environment const & env, std::string const & pattern, declaration const & d) {
    if (auto it = is_essentially_atomic(env, d.get_name())) {
        std::string it_str = it->to_string();
        // if pattern "perfectly" matches beginning of declaration name, we just display d on the top of the list
        if (it_str.compare(0, pattern.size(), pattern) == 0)
            return it;
    } else {
        std::string d_str = d.get_name().to_string();
        if (d_str.compare(0, pattern.size(), pattern) == 0)
            return optional<name>(d.get_name());
    }
    return optional<name>();
}

void filter_completions(std::string const & pattern, std::vector<pair<std::string, name>> & selected,
                        std::vector<json> & completions, unsigned max_results, std::function<json(name)> serialize) {
    unsigned max_errors = get_fuzzy_match_max_errors(pattern.size());
    std::vector<pair<name, name>> exact_matches;
    bitap_fuzzy_search matcher(pattern, max_errors);
    unsigned num_results = 0;
    unsigned sz = selected.size();
    if (sz == 1) {
        completions.push_back(serialize(selected[0].second));
    } else if (sz > 1) {
        std::sort(selected.begin(), selected.end());
        std::vector<pair<std::string, name>> next_selected;
        for (unsigned k = 0; k <= max_errors && num_results < max_results; k++) {
            bitap_fuzzy_search matcher(pattern, k);
            for (auto const & s : selected) {
                if (matcher.match(s.first)) {
                    completions.push_back(serialize(s.second));
                    num_results++;
                    if (num_results >= max_results)
                        break;
                } else {
                    next_selected.push_back(s);
                }
            }
            std::swap(selected, next_selected);
            next_selected.clear();
        }
    }
}

std::vector<json> get_decl_completions(std::string const & pattern, environment const & env, options const & opts) {
    std::vector<json> completions;

    unsigned max_results = get_auto_completion_max_results(opts);
    unsigned max_errors = get_fuzzy_match_max_errors(pattern.size());
    std::vector<pair<name, name>> exact_matches;
    std::vector<pair<std::string, name>> selected;
    bitap_fuzzy_search matcher(pattern, max_errors);
    env.for_each_declaration([&](declaration const & d) {
        if (is_projection(env, d.get_name())) {
            auto s_name = d.get_name().get_prefix();
            if (is_class(env, s_name))
                return;
        }
        if (is_internal_name(d.get_name())) {
            return;
        }
        if (auto it = exact_prefix_match(env, pattern, d)) {
            exact_matches.emplace_back(*it, d.get_name());
        } else {
            std::string text = d.get_name().to_string();
            if (matcher.match(text))
                selected.emplace_back(text, d.get_name());
        }
    });
    unsigned num_results = 0;
    if (!exact_matches.empty()) {
        std::sort(exact_matches.begin(), exact_matches.end(),
                  [](pair<name, name> const & p1, pair<name, name> const & p2) {
                      return p1.first.size() < p2.first.size();
                  });
        for (pair<name, name> const & p : exact_matches) {
            completions.push_back(serialize_decl(p.first, p.second, env, opts));
            num_results++;
            if (num_results >= max_results)
                break;
        }
    }
    filter_completions(pattern, selected, completions, max_results - num_results, [&](name const & n) {
        return serialize_decl(n, env, opts);
    });
    return completions;
}

std::vector<json> get_option_completions(std::string const & pattern, options const & opts) {
    unsigned max_results = get_auto_completion_max_results(opts);
    unsigned max_errors = get_fuzzy_match_max_errors(pattern.size());
    std::vector<pair<std::string, name>> selected;
    bitap_fuzzy_search matcher(pattern, max_errors);
    std::vector<json> completions;

    get_option_declarations().for_each([&](name const & n, option_declaration const &) {
        std::string text = n.to_string();
        if (matcher.match(text))
            selected.emplace_back(text, n);
    });
    filter_completions(pattern, selected, completions, max_results, [&](name const & n) {
        json completion;
        completion["text"] = n.to_string();
        std::stringstream ss;
        get_option_declarations().find(n)->display_value(ss, opts);
        completion["type"] = ss.str();
        return completion;
    });
    return completions;
}

std::vector<json> get_import_completions(std::string const & pattern, std::string const & curr_dir,
                                         options const & opts) {
    unsigned max_results = get_auto_completion_max_results(opts);
    unsigned max_errors = get_fuzzy_match_max_errors(pattern.size());
    std::vector<pair<std::string, name>> selected;
    bitap_fuzzy_search matcher(pattern, max_errors);
    std::vector<json> completions;

    optional<unsigned> depth;
    if (pattern.size() && pattern[0] == '.') {
        unsigned i = 1;
        while (i < pattern.size() && pattern[i] == '.')
            i++;
        depth = {i - 1};
    }
    std::vector<std::string> imports;
    find_imports(curr_dir, depth, imports);

    for (auto const & candidate : imports) {
        if (matcher.match(candidate))
            selected.emplace_back(candidate, candidate);
    }
    filter_completions(pattern, selected, completions, max_results, [&](name const & n) {
        json completion;
        completion["text"] = n.to_string();
        return completion;
    });
    return completions;
}

std::vector<json> get_interactive_tactic_completions(std::string const & pattern, name const & tac_class,
                                                     environment const & env, options const & opts) {
    std::vector<json> completions;

    unsigned max_results = get_auto_completion_max_results(opts);
    unsigned max_errors = get_fuzzy_match_max_errors(pattern.size());
    std::vector<pair<std::string, name>> selected;
    bitap_fuzzy_search matcher(pattern, max_errors);
    name namespc = tac_class + name("interactive");
    env.for_each_declaration([&](declaration const & d) {
        auto const & n = d.get_name();
        if (n.get_prefix() == namespc && n.is_string() && matcher.match(n.get_string())) {
            selected.emplace_back(n.get_string(), n);
        }
    });
    filter_completions(pattern, selected, completions, max_results, [&](name const & n) {
        return serialize_decl(n.get_string(), n, env, opts);
    });
    // append regular completions
    for (auto candidate : get_decl_completions(pattern, env, opts))
        completions.push_back(candidate);
    return completions;
}

std::vector<json> get_attribute_completions(std::string const & pattern, environment const & env,
                                            options const & opts) {
    unsigned max_results = get_auto_completion_max_results(opts);
    unsigned max_errors = get_fuzzy_match_max_errors(pattern.size());
    std::vector<pair<std::string, name>> selected;
    bitap_fuzzy_search matcher(pattern, max_errors);
    std::vector<json> completions;

    buffer<attribute const *> attrs;
    get_attributes(env, attrs);
    for (auto const & attr : attrs) {
        auto s = attr->get_name().to_string();
        if (matcher.match(s))
            selected.emplace_back(s, attr->get_name());
    }
    filter_completions(pattern, selected, completions, max_results, [&](name const & n) {
        json completion;
        completion["text"] = n.to_string();
        completion["type"] = get_attribute(env, n).get_description();
        return completion;
    });
    return completions;
}

std::vector<json> get_namespace_completions(std::string const & pattern, environment const & env,
                                            options const & opts) {
    unsigned max_results = get_auto_completion_max_results(opts);
    unsigned max_errors = get_fuzzy_match_max_errors(pattern.size());
    std::vector<pair<std::string, name>> selected;
    bitap_fuzzy_search matcher(pattern, max_errors);
    std::vector<json> completions;

    for (auto const & ns : get_namespace_completion_candidates(env)) {
        if (ns.is_anonymous())
            continue;
        auto s = ns.to_string();
        if (matcher.match(s))
            selected.emplace_back(s, ns);
    }
    filter_completions(pattern, selected, completions, max_results, [&](name const & n) {
        json completion;
        completion["text"] = n.to_string();
        return completion;
    });
    return completions;
}
}
#endif
<commit_msg>feat(shell/completion): prefer exact prefix matches for all completion modes<commit_after>/*
Copyright (c) 2016 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.

Authors: Gabriel Ebner, Leonardo de Moura, Sebastian Ullrich
*/
#if defined(LEAN_JSON)
#include <algorithm>
#include <string>
#include <vector>
#include "util/lean_path.h"
#include "util/sexpr/option_declarations.h"
#include "util/bitap_fuzzy_search.h"
#include "library/protected.h"
#include "library/util.h"
#include "library/scoped_ext.h"
#include "library/class.h"
#include "frontends/lean/util.h"
#include "shell/server.h"
#include "shell/completion.h"

namespace lean {

#define LEAN_FUZZY_MAX_ERRORS          3
#define LEAN_FUZZY_MAX_ERRORS_FACTOR   3

/** \brief Return an (atomic) name if \c n can be referenced by this atomic
    name in the given environment. */
optional<name> is_essentially_atomic(environment const & env, name const & n) {
    if (n.is_atomic())
        return optional<name>(n);
    list<name> const & ns_list = get_namespaces(env);
    for (name const & ns : ns_list) {
        if (is_prefix_of(ns, n)) {
            auto n_prime = n.replace_prefix(ns, name());
            if (n_prime.is_atomic() && !is_protected(env, n))
                return optional<name>(n_prime);
            break;
        }
    }
    if (auto it = is_uniquely_aliased(env, n))
        if (it->is_atomic())
            return it;
    return optional<name>();
}

unsigned get_fuzzy_match_max_errors(unsigned prefix_sz) {
    unsigned r = (prefix_sz / LEAN_FUZZY_MAX_ERRORS_FACTOR);
    if (r > LEAN_FUZZY_MAX_ERRORS)
        return LEAN_FUZZY_MAX_ERRORS;
    return r;
}

optional<name> exact_prefix_match(environment const & env, std::string const & pattern, declaration const & d) {
    if (auto it = is_essentially_atomic(env, d.get_name())) {
        std::string it_str = it->to_string();
        // if pattern "perfectly" matches beginning of declaration name, we just display d on the top of the list
        if (it_str.compare(0, pattern.size(), pattern) == 0)
            return it;
    }
    return optional<name>();
}

void filter_completions(std::string const & pattern, std::vector<pair<std::string, name>> & selected,
                        std::vector<json> & completions, unsigned max_results, std::function<json(name)> serialize) {
    unsigned max_errors = get_fuzzy_match_max_errors(pattern.size());
    std::vector<pair<name, name>> exact_matches;
    bitap_fuzzy_search matcher(pattern, max_errors);
    unsigned num_results = 0;
    unsigned sz = selected.size();
    if (sz == 1) {
        completions.push_back(serialize(selected[0].second));
    } else if (sz > 1) {
        std::sort(selected.begin(), selected.end());
        std::vector<pair<std::string, name>> next_selected;
        auto process = [&](pair<std::string, name> const & s, bool select) {
            if (select) {
                completions.push_back(serialize(s.second));
                num_results++;
                if (num_results >= max_results)
                    return false;
            } else {
                next_selected.push_back(s);
            }
            return true;
        };

        // 1. exact prefix matches
        for (auto const & s : selected) {
            if (!process(s, s.first.compare(0, pattern.size(), pattern) == 0))
                break;
        }
        std::swap(selected, next_selected);
        next_selected.clear();

        // 2. fuzzy matches by increasing error count
        for (unsigned k = 0; k <= max_errors && num_results < max_results; k++) {
            bitap_fuzzy_search matcher(pattern, k);
            for (auto const & s : selected) {
                if (!process(s, matcher.match(s.first)))
                    break;
            }
            std::swap(selected, next_selected);
            next_selected.clear();
        }
    }
}

std::vector<json> get_decl_completions(std::string const & pattern, environment const & env, options const & opts) {
    std::vector<json> completions;

    unsigned max_results = get_auto_completion_max_results(opts);
    unsigned max_errors = get_fuzzy_match_max_errors(pattern.size());
    std::vector<pair<name, name>> exact_matches;
    std::vector<pair<std::string, name>> selected;
    bitap_fuzzy_search matcher(pattern, max_errors);
    env.for_each_declaration([&](declaration const & d) {
        if (is_projection(env, d.get_name())) {
            auto s_name = d.get_name().get_prefix();
            if (is_class(env, s_name))
                return;
        }
        if (is_internal_name(d.get_name())) {
            return;
        }
        if (auto it = exact_prefix_match(env, pattern, d)) {
            exact_matches.emplace_back(*it, d.get_name());
        } else {
            std::string text = d.get_name().to_string();
            if (matcher.match(text))
                selected.emplace_back(text, d.get_name());
        }
    });
    unsigned num_results = 0;
    if (!exact_matches.empty()) {
        std::sort(exact_matches.begin(), exact_matches.end(),
                  [](pair<name, name> const & p1, pair<name, name> const & p2) {
                      return p1.first.size() < p2.first.size();
                  });
        for (pair<name, name> const & p : exact_matches) {
            completions.push_back(serialize_decl(p.first, p.second, env, opts));
            num_results++;
            if (num_results >= max_results)
                break;
        }
    }
    filter_completions(pattern, selected, completions, max_results - num_results, [&](name const & n) {
        return serialize_decl(n, env, opts);
    });
    return completions;
}

std::vector<json> get_option_completions(std::string const & pattern, options const & opts) {
    unsigned max_results = get_auto_completion_max_results(opts);
    unsigned max_errors = get_fuzzy_match_max_errors(pattern.size());
    std::vector<pair<std::string, name>> selected;
    bitap_fuzzy_search matcher(pattern, max_errors);
    std::vector<json> completions;

    get_option_declarations().for_each([&](name const & n, option_declaration const &) {
        std::string text = n.to_string();
        if (matcher.match(text))
            selected.emplace_back(text, n);
    });
    filter_completions(pattern, selected, completions, max_results, [&](name const & n) {
        json completion;
        completion["text"] = n.to_string();
        std::stringstream ss;
        get_option_declarations().find(n)->display_value(ss, opts);
        completion["type"] = ss.str();
        return completion;
    });
    return completions;
}

std::vector<json> get_import_completions(std::string const & pattern, std::string const & curr_dir,
                                         options const & opts) {
    unsigned max_results = get_auto_completion_max_results(opts);
    unsigned max_errors = get_fuzzy_match_max_errors(pattern.size());
    std::vector<pair<std::string, name>> selected;
    bitap_fuzzy_search matcher(pattern, max_errors);
    std::vector<json> completions;

    optional<unsigned> depth;
    if (pattern.size() && pattern[0] == '.') {
        unsigned i = 1;
        while (i < pattern.size() && pattern[i] == '.')
            i++;
        depth = {i - 1};
    }
    std::vector<std::string> imports;
    find_imports(curr_dir, depth, imports);

    for (auto const & candidate : imports) {
        if (matcher.match(candidate))
            selected.emplace_back(candidate, candidate);
    }
    filter_completions(pattern, selected, completions, max_results, [&](name const & n) {
        json completion;
        completion["text"] = n.to_string();
        return completion;
    });
    return completions;
}

std::vector<json> get_interactive_tactic_completions(std::string const & pattern, name const & tac_class,
                                                     environment const & env, options const & opts) {
    std::vector<json> completions;

    unsigned max_results = get_auto_completion_max_results(opts);
    unsigned max_errors = get_fuzzy_match_max_errors(pattern.size());
    std::vector<pair<std::string, name>> selected;
    bitap_fuzzy_search matcher(pattern, max_errors);
    name namespc = tac_class + name("interactive");
    env.for_each_declaration([&](declaration const & d) {
        auto const & n = d.get_name();
        if (n.get_prefix() == namespc && n.is_string() && matcher.match(n.get_string())) {
            selected.emplace_back(n.get_string(), n);
        }
    });
    filter_completions(pattern, selected, completions, max_results, [&](name const & n) {
        return serialize_decl(n.get_string(), n, env, opts);
    });
    // append regular completions
    for (auto candidate : get_decl_completions(pattern, env, opts))
        completions.push_back(candidate);
    return completions;
}

std::vector<json> get_attribute_completions(std::string const & pattern, environment const & env,
                                            options const & opts) {
    unsigned max_results = get_auto_completion_max_results(opts);
    unsigned max_errors = get_fuzzy_match_max_errors(pattern.size());
    std::vector<pair<std::string, name>> selected;
    bitap_fuzzy_search matcher(pattern, max_errors);
    std::vector<json> completions;

    buffer<attribute const *> attrs;
    get_attributes(env, attrs);
    for (auto const & attr : attrs) {
        auto s = attr->get_name().to_string();
        if (matcher.match(s))
            selected.emplace_back(s, attr->get_name());
    }
    filter_completions(pattern, selected, completions, max_results, [&](name const & n) {
        json completion;
        completion["text"] = n.to_string();
        completion["type"] = get_attribute(env, n).get_description();
        return completion;
    });
    return completions;
}

std::vector<json> get_namespace_completions(std::string const & pattern, environment const & env,
                                            options const & opts) {
    unsigned max_results = get_auto_completion_max_results(opts);
    unsigned max_errors = get_fuzzy_match_max_errors(pattern.size());
    std::vector<pair<std::string, name>> selected;
    bitap_fuzzy_search matcher(pattern, max_errors);
    std::vector<json> completions;

    for (auto const & ns : get_namespace_completion_candidates(env)) {
        if (ns.is_anonymous())
            continue;
        auto s = ns.to_string();
        if (matcher.match(s))
            selected.emplace_back(s, ns);
    }
    filter_completions(pattern, selected, completions, max_results, [&](name const & n) {
        json completion;
        completion["text"] = n.to_string();
        return completion;
    });
    return completions;
}
}
#endif
<|endoftext|>


<commit_before>// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ppapi/tests/test_broker.h"

#if defined(_MSC_VER)
#define OS_WIN 1
#include <windows.h>
#else
#define OS_POSIX 1
#include <errno.h>
#endif

#include <cstdio>
#include <cstring>
#include <fstream>
#include <limits>

#include "ppapi/c/pp_errors.h"
#include "ppapi/c/trusted/ppp_broker.h"
#include "ppapi/c/trusted/ppb_broker_trusted.h"
#include "ppapi/tests/test_utils.h"
#include "ppapi/tests/testing_instance.h"

REGISTER_TEST_CASE(Broker);

namespace {

const char kHelloMessage[] = "Hello Plugin! This is Broker!";
// Message sent from broker to plugin if the broker is unsandboxed.
const char kBrokerUnsandboxed[] = "Broker is Unsandboxed!";
// Message sent from broker to plugin if the broker is sandboxed. This message
// needs to be longer than |kBrokerUnsandboxed| because the plugin is expecting
// |kBrokerUnsandboxed|. If it's shorter and the broker doesn't close its handle
// properly the plugin will hang waiting for all data of |kBrokerUnsandboxed| to
// be read.
const char kBrokerSandboxed[] = "Broker is Sandboxed! Verification failed!";

#if defined(OS_WIN)
typedef HANDLE PlatformFile;
const PlatformFile kInvalidPlatformFileValue = INVALID_HANDLE_VALUE;
const int32_t kInvalidHandle = static_cast<int32_t>(
    reinterpret_cast<intptr_t>(INVALID_HANDLE_VALUE));
#elif defined(OS_POSIX)
typedef int PlatformFile;
const PlatformFile kInvalidPlatformFileValue = -1;
const int32_t kInvalidHandle = -1;
#endif

PlatformFile IntToPlatformFile(int32_t handle) {
#if defined(OS_WIN)
  return reinterpret_cast<HANDLE>(static_cast<intptr_t>(handle));
#elif defined(OS_POSIX)
  return handle;
#endif
}

#if defined(OS_POSIX)
#define HANDLE_EINTR(x) ({ \
  typeof(x) __eintr_result__; \
  do { \
    __eintr_result__ = x; \
  } while (__eintr_result__ == -1 && errno == EINTR); \
  __eintr_result__;\
})
#endif

bool ReadMessage(PlatformFile file, size_t message_len, char* message) {
#if defined(OS_WIN)
  assert(message_len < std::numeric_limits<DWORD>::max());
  DWORD read = 0;
  const DWORD size = static_cast<DWORD>(message_len);
  return ::ReadFile(file, message, size, &read, NULL) && read == size;
#elif defined(OS_POSIX)
  assert(message_len <
         static_cast<size_t>(std::numeric_limits<ssize_t>::max()));
  size_t total_read = 0;
  while (total_read < message_len) {
    ssize_t read = HANDLE_EINTR(::read(file, message + total_read,
                                       message_len - total_read));
    if (read <= 0)
      break;
    total_read += read;
  }
  return total_read == message_len;
#endif
}

bool WriteMessage(PlatformFile file, size_t message_len, const char* message) {
#if defined(OS_WIN)
  assert(message_len < std::numeric_limits<DWORD>::max());
  DWORD written = 0;
  const DWORD size = static_cast<DWORD>(message_len);
  return ::WriteFile(file, message, size, &written, NULL) && written == size;
#elif defined(OS_POSIX)
  assert(message_len <
         static_cast<size_t>(std::numeric_limits<ssize_t>::max()));
  size_t total_written = 0;
  while (total_written < message_len) {
    ssize_t written = HANDLE_EINTR(::write(file, message + total_written,
                                           message_len - total_written));
    if (written <= 0)
      break;
    total_written += written;
  }
  return total_written == message_len;
#endif
}

bool VerifyMessage(PlatformFile file, size_t message_len, const char* message) {
  char* message_received = new char[message_len];
  bool success = ReadMessage(file, message_len, message_received) &&
                 !::strcmp(message_received, message);
  delete [] message_received;
  return success;
}

bool ClosePlatformFile(PlatformFile file) {
#if defined(OS_WIN)
  return !!::CloseHandle(file);
#elif defined(OS_POSIX)
  return !::close(file);
#endif
}

bool VerifyIsUnsandboxed() {
#if defined(OS_WIN)
  FILE* file = NULL;
  wchar_t temp_path[MAX_PATH] = {'\0'};
  wchar_t file_name[MAX_PATH] = {'\0'};
  if (!::GetTempPath(MAX_PATH, temp_path) ||
      !::GetTempFileName(temp_path, L"test_pepper_broker", 0, file_name) ||
      ::_wfopen_s(&file, file_name, L"w"))
    return false;

  if (::fclose(file)) {
    ::DeleteFile(file_name);
    return false;
  }

  return !!::DeleteFile(file_name);
#elif defined(OS_POSIX)
  char file_name[] = "/tmp/test_pepper_broker_XXXXXX";
  int fd = ::mkstemp(file_name);
  if (-1 == fd)
    return false;

  if (::close(fd)) {
    ::remove(file_name);
    return false;
  }

  return !::remove(file_name);
#endif
}

// Callback in the broker when a new broker connection occurs.
int32_t OnInstanceConnected(PP_Instance instance, int32_t handle) {
  PlatformFile file = IntToPlatformFile(handle);
  if (file == kInvalidPlatformFileValue)
    return PP_ERROR_FAILED;

  // Send hello message.
  if (!WriteMessage(file, sizeof(kHelloMessage), kHelloMessage)) {
    ClosePlatformFile(file);
    return PP_ERROR_FAILED;
  }

  // Verify broker is not sandboxed and send result to plugin over the pipe.
  if (VerifyIsUnsandboxed()) {
    if (!WriteMessage(file, sizeof(kBrokerUnsandboxed), kBrokerUnsandboxed)) {
      ClosePlatformFile(file);
      return PP_ERROR_FAILED;
    }
  } else {
    if (!WriteMessage(file, sizeof(kBrokerSandboxed), kBrokerSandboxed)) {
      ClosePlatformFile(file);
      return PP_ERROR_FAILED;
    }
  }

  if (!ClosePlatformFile(file))
    return PP_ERROR_FAILED;

  return PP_OK;
}

}  // namespace

PP_EXPORT int32_t PPP_InitializeBroker(
    PP_ConnectInstance_Func* connect_instance_func) {
  *connect_instance_func = &OnInstanceConnected;
  return PP_OK;
}

PP_EXPORT void PPP_ShutdownBroker() {}

TestBroker::TestBroker(TestingInstance* instance)
    : TestCase(instance),
      broker_interface_(NULL) {
}

bool TestBroker::Init() {
  broker_interface_ = static_cast<PPB_BrokerTrusted const*>(
      pp::Module::Get()->GetBrowserInterface(PPB_BROKER_TRUSTED_INTERFACE));
  return !!broker_interface_;
}

void TestBroker::RunTests(const std::string& filter) {
  RUN_TEST(Create, filter);
  RUN_TEST(Create, filter);
  RUN_TEST(GetHandleFailure, filter);
  RUN_TEST(ConnectFailure, filter);
#if !defined(OS_WIN)  // This is broken on Windows. See http://crbug.com/103975.
  RUN_TEST(ConnectAndPipe, filter);
#endif
}

std::string TestBroker::TestCreate() {
  // Very simplistic test to make sure we can create a broker interface.
  PP_Resource broker = broker_interface_->CreateTrusted(
      instance_->pp_instance());
  ASSERT_TRUE(broker);

  ASSERT_FALSE(broker_interface_->IsBrokerTrusted(0));
  ASSERT_TRUE(broker_interface_->IsBrokerTrusted(broker));

  PASS();
}

// Test connection on invalid resource.
std::string TestBroker::TestConnectFailure() {
  // Callback NOT force async. Connect should fail.  The callback will not be
  // posted so there's no need to wait for the callback to complete.
  TestCompletionCallback cb_1(instance_->pp_instance(), false);
  ASSERT_EQ(PP_ERROR_BADRESOURCE,
            broker_interface_->Connect(
                0, pp::CompletionCallback(cb_1).pp_completion_callback()));

  // Callback force async. Connect will return PP_OK_COMPLETIONPENDING and the
  // callback will be posted.  However, the callback should fail.
  TestCompletionCallback cb_2(instance_->pp_instance(), true);
  ASSERT_EQ(PP_OK_COMPLETIONPENDING,
            broker_interface_->Connect(
                0, pp::CompletionCallback(cb_2).pp_completion_callback()));
  ASSERT_EQ(PP_ERROR_BADRESOURCE, cb_2.WaitForResult());

  PASS();
}

std::string TestBroker::TestGetHandleFailure() {
  int32_t handle = kInvalidHandle;

  // Test getting the handle for an invalid resource.
  ASSERT_EQ(PP_ERROR_BADRESOURCE, broker_interface_->GetHandle(0, &handle));

  // Connect hasn't been called so this should fail.
  PP_Resource broker = broker_interface_->CreateTrusted(
      instance_->pp_instance());
  ASSERT_TRUE(broker);
  ASSERT_EQ(PP_ERROR_FAILED, broker_interface_->GetHandle(broker, &handle));

  PASS();
}

std::string TestBroker::TestConnectAndPipe() {
  PP_Resource broker = broker_interface_->CreateTrusted(
      instance_->pp_instance());
  ASSERT_TRUE(broker);

  TestCompletionCallback cb_3(instance_->pp_instance());
  ASSERT_EQ(PP_OK_COMPLETIONPENDING,
            broker_interface_->Connect(
                broker, pp::CompletionCallback(cb_3).pp_completion_callback()));
  ASSERT_EQ(PP_OK, cb_3.WaitForResult());

  int32_t handle = kInvalidHandle;
  ASSERT_EQ(PP_OK, broker_interface_->GetHandle(broker, &handle));
  ASSERT_NE(kInvalidHandle, handle);

  PlatformFile file = IntToPlatformFile(handle);
  ASSERT_TRUE(VerifyMessage(file, sizeof(kHelloMessage), kHelloMessage));
  ASSERT_TRUE(VerifyMessage(file, sizeof(kBrokerUnsandboxed),
                            kBrokerUnsandboxed));

  ASSERT_TRUE(ClosePlatformFile(file));

  PASS();
}
<commit_msg>Add EINTR handling on ::close() calls in ppapi broker ui test.<commit_after>// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ppapi/tests/test_broker.h"

#if defined(_MSC_VER)
#define OS_WIN 1
#include <windows.h>
#else
#define OS_POSIX 1
#include <errno.h>
#endif

#include <cstdio>
#include <cstring>
#include <fstream>
#include <limits>

#include "ppapi/c/pp_errors.h"
#include "ppapi/c/trusted/ppp_broker.h"
#include "ppapi/c/trusted/ppb_broker_trusted.h"
#include "ppapi/tests/test_utils.h"
#include "ppapi/tests/testing_instance.h"

REGISTER_TEST_CASE(Broker);

namespace {

const char kHelloMessage[] = "Hello Plugin! This is Broker!";
// Message sent from broker to plugin if the broker is unsandboxed.
const char kBrokerUnsandboxed[] = "Broker is Unsandboxed!";
// Message sent from broker to plugin if the broker is sandboxed. This message
// needs to be longer than |kBrokerUnsandboxed| because the plugin is expecting
// |kBrokerUnsandboxed|. If it's shorter and the broker doesn't close its handle
// properly the plugin will hang waiting for all data of |kBrokerUnsandboxed| to
// be read.
const char kBrokerSandboxed[] = "Broker is Sandboxed! Verification failed!";

#if defined(OS_WIN)
typedef HANDLE PlatformFile;
const PlatformFile kInvalidPlatformFileValue = INVALID_HANDLE_VALUE;
const int32_t kInvalidHandle = static_cast<int32_t>(
    reinterpret_cast<intptr_t>(INVALID_HANDLE_VALUE));
#elif defined(OS_POSIX)
typedef int PlatformFile;
const PlatformFile kInvalidPlatformFileValue = -1;
const int32_t kInvalidHandle = -1;
#endif

PlatformFile IntToPlatformFile(int32_t handle) {
#if defined(OS_WIN)
  return reinterpret_cast<HANDLE>(static_cast<intptr_t>(handle));
#elif defined(OS_POSIX)
  return handle;
#endif
}

#if defined(OS_POSIX)
#define HANDLE_EINTR(x) ({ \
  typeof(x) __eintr_result__; \
  do { \
    __eintr_result__ = x; \
  } while (__eintr_result__ == -1 && errno == EINTR); \
  __eintr_result__;\
})
#endif

bool ReadMessage(PlatformFile file, size_t message_len, char* message) {
#if defined(OS_WIN)
  assert(message_len < std::numeric_limits<DWORD>::max());
  DWORD read = 0;
  const DWORD size = static_cast<DWORD>(message_len);
  return ::ReadFile(file, message, size, &read, NULL) && read == size;
#elif defined(OS_POSIX)
  assert(message_len <
         static_cast<size_t>(std::numeric_limits<ssize_t>::max()));
  size_t total_read = 0;
  while (total_read < message_len) {
    ssize_t read = HANDLE_EINTR(::read(file, message + total_read,
                                       message_len - total_read));
    if (read <= 0)
      break;
    total_read += read;
  }
  return total_read == message_len;
#endif
}

bool WriteMessage(PlatformFile file, size_t message_len, const char* message) {
#if defined(OS_WIN)
  assert(message_len < std::numeric_limits<DWORD>::max());
  DWORD written = 0;
  const DWORD size = static_cast<DWORD>(message_len);
  return ::WriteFile(file, message, size, &written, NULL) && written == size;
#elif defined(OS_POSIX)
  assert(message_len <
         static_cast<size_t>(std::numeric_limits<ssize_t>::max()));
  size_t total_written = 0;
  while (total_written < message_len) {
    ssize_t written = HANDLE_EINTR(::write(file, message + total_written,
                                           message_len - total_written));
    if (written <= 0)
      break;
    total_written += written;
  }
  return total_written == message_len;
#endif
}

bool VerifyMessage(PlatformFile file, size_t message_len, const char* message) {
  char* message_received = new char[message_len];
  bool success = ReadMessage(file, message_len, message_received) &&
                 !::strcmp(message_received, message);
  delete [] message_received;
  return success;
}

bool ClosePlatformFile(PlatformFile file) {
#if defined(OS_WIN)
  return !!::CloseHandle(file);
#elif defined(OS_POSIX)
  return !HANDLE_EINTR(::close(file));
#endif
}

bool VerifyIsUnsandboxed() {
#if defined(OS_WIN)
  FILE* file = NULL;
  wchar_t temp_path[MAX_PATH] = {'\0'};
  wchar_t file_name[MAX_PATH] = {'\0'};
  if (!::GetTempPath(MAX_PATH, temp_path) ||
      !::GetTempFileName(temp_path, L"test_pepper_broker", 0, file_name) ||
      ::_wfopen_s(&file, file_name, L"w"))
    return false;

  if (::fclose(file)) {
    ::DeleteFile(file_name);
    return false;
  }

  return !!::DeleteFile(file_name);
#elif defined(OS_POSIX)
  char file_name[] = "/tmp/test_pepper_broker_XXXXXX";
  int fd = ::mkstemp(file_name);
  if (-1 == fd)
    return false;

  if (HANDLE_EINTR(::close(fd))) {
    ::remove(file_name);
    return false;
  }

  return !::remove(file_name);
#endif
}

// Callback in the broker when a new broker connection occurs.
int32_t OnInstanceConnected(PP_Instance instance, int32_t handle) {
  PlatformFile file = IntToPlatformFile(handle);
  if (file == kInvalidPlatformFileValue)
    return PP_ERROR_FAILED;

  // Send hello message.
  if (!WriteMessage(file, sizeof(kHelloMessage), kHelloMessage)) {
    ClosePlatformFile(file);
    return PP_ERROR_FAILED;
  }

  // Verify broker is not sandboxed and send result to plugin over the pipe.
  if (VerifyIsUnsandboxed()) {
    if (!WriteMessage(file, sizeof(kBrokerUnsandboxed), kBrokerUnsandboxed)) {
      ClosePlatformFile(file);
      return PP_ERROR_FAILED;
    }
  } else {
    if (!WriteMessage(file, sizeof(kBrokerSandboxed), kBrokerSandboxed)) {
      ClosePlatformFile(file);
      return PP_ERROR_FAILED;
    }
  }

  if (!ClosePlatformFile(file))
    return PP_ERROR_FAILED;

  return PP_OK;
}

}  // namespace

PP_EXPORT int32_t PPP_InitializeBroker(
    PP_ConnectInstance_Func* connect_instance_func) {
  *connect_instance_func = &OnInstanceConnected;
  return PP_OK;
}

PP_EXPORT void PPP_ShutdownBroker() {}

TestBroker::TestBroker(TestingInstance* instance)
    : TestCase(instance),
      broker_interface_(NULL) {
}

bool TestBroker::Init() {
  broker_interface_ = static_cast<PPB_BrokerTrusted const*>(
      pp::Module::Get()->GetBrowserInterface(PPB_BROKER_TRUSTED_INTERFACE));
  return !!broker_interface_;
}

void TestBroker::RunTests(const std::string& filter) {
  RUN_TEST(Create, filter);
  RUN_TEST(Create, filter);
  RUN_TEST(GetHandleFailure, filter);
  RUN_TEST(ConnectFailure, filter);
#if !defined(OS_WIN)  // This is broken on Windows. See http://crbug.com/103975.
  RUN_TEST(ConnectAndPipe, filter);
#endif
}

std::string TestBroker::TestCreate() {
  // Very simplistic test to make sure we can create a broker interface.
  PP_Resource broker = broker_interface_->CreateTrusted(
      instance_->pp_instance());
  ASSERT_TRUE(broker);

  ASSERT_FALSE(broker_interface_->IsBrokerTrusted(0));
  ASSERT_TRUE(broker_interface_->IsBrokerTrusted(broker));

  PASS();
}

// Test connection on invalid resource.
std::string TestBroker::TestConnectFailure() {
  // Callback NOT force async. Connect should fail.  The callback will not be
  // posted so there's no need to wait for the callback to complete.
  TestCompletionCallback cb_1(instance_->pp_instance(), false);
  ASSERT_EQ(PP_ERROR_BADRESOURCE,
            broker_interface_->Connect(
                0, pp::CompletionCallback(cb_1).pp_completion_callback()));

  // Callback force async. Connect will return PP_OK_COMPLETIONPENDING and the
  // callback will be posted.  However, the callback should fail.
  TestCompletionCallback cb_2(instance_->pp_instance(), true);
  ASSERT_EQ(PP_OK_COMPLETIONPENDING,
            broker_interface_->Connect(
                0, pp::CompletionCallback(cb_2).pp_completion_callback()));
  ASSERT_EQ(PP_ERROR_BADRESOURCE, cb_2.WaitForResult());

  PASS();
}

std::string TestBroker::TestGetHandleFailure() {
  int32_t handle = kInvalidHandle;

  // Test getting the handle for an invalid resource.
  ASSERT_EQ(PP_ERROR_BADRESOURCE, broker_interface_->GetHandle(0, &handle));

  // Connect hasn't been called so this should fail.
  PP_Resource broker = broker_interface_->CreateTrusted(
      instance_->pp_instance());
  ASSERT_TRUE(broker);
  ASSERT_EQ(PP_ERROR_FAILED, broker_interface_->GetHandle(broker, &handle));

  PASS();
}

std::string TestBroker::TestConnectAndPipe() {
  PP_Resource broker = broker_interface_->CreateTrusted(
      instance_->pp_instance());
  ASSERT_TRUE(broker);

  TestCompletionCallback cb_3(instance_->pp_instance());
  ASSERT_EQ(PP_OK_COMPLETIONPENDING,
            broker_interface_->Connect(
                broker, pp::CompletionCallback(cb_3).pp_completion_callback()));
  ASSERT_EQ(PP_OK, cb_3.WaitForResult());

  int32_t handle = kInvalidHandle;
  ASSERT_EQ(PP_OK, broker_interface_->GetHandle(broker, &handle));
  ASSERT_NE(kInvalidHandle, handle);

  PlatformFile file = IntToPlatformFile(handle);
  ASSERT_TRUE(VerifyMessage(file, sizeof(kHelloMessage), kHelloMessage));
  ASSERT_TRUE(VerifyMessage(file, sizeof(kBrokerUnsandboxed),
                            kBrokerUnsandboxed));

  ASSERT_TRUE(ClosePlatformFile(file));

  PASS();
}
<|endoftext|>


<commit_before>/// \file ROOT/TDrawingAttrs.hxx
/// \ingroup Gpad ROOT7
/// \author Axel Naumann <axel@cern.ch>
/// \date 2017-09-26
/// \warning This is part of the ROOT 7 prototype! It will change without notice. It might trigger earthquakes. Feedback
/// is welcome!

/*************************************************************************
 * Copyright (C) 1995-2017, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

#ifndef ROOT7_TDrawingAttrs
#define ROOT7_TDrawingAttrs

#include <ROOT/TStyle.hxx>

#include <memory>
#include <string>
#include <vector>

namespace ROOT {
namespace Experimental {

class TColor;
class TStringAttr;

///\{
/// Initialize an attribute `val` from a string value.
///
///\param[in] name - the attribute name, for diagnostic purposes.
///\param[in] strval - the attribute value as a string.
///\param[out] val - the value to be initialized.
void InitializeAttrFromString(const std::string &name, const std::string &strval, int& val);
void InitializeAttrFromString(const std::string &name, const std::string &strval, long long& val);
void InitializeAttrFromString(const std::string &name, const std::string &strval, float& val);
void InitializeAttrFromString(const std::string &name, const std::string &strval, std::string& val);
///\}

/** \class ROOT::Experimental::TDrawingAttrOrRef
 A wrapper for a graphics attribute, for instance a `TColor`.
 The `TTopmostPad` keeps track of shared attributes used by multiple drawing options by means of
 `weak_ptr`s; `TDrawingAttrOrRef`s hold `shared_ptr`s to these.
 The reference into the table of the shared attributes is wrapped into the reference of the `TDrawingAttrOrRef`
 to make them type-safe (i.e. distinct for `TColor`, `long long` and `double`).
 */

template <class ATTR>
class TDrawingAttrOrRef {
private:
   std::shared_ptr<ATTR> fPtr; ///< The shared_ptr, shared with the relevant attribute table of `TTopmostPad`.
   ATTR fAttr; ///< The attribute value, used if `!fPtr`.

   /// Whether this attribute is shared (through `TTopmostPad`'s attribute table) with other `TDrawingAttrOrRef`
   /// objects.
   bool IsShared() const { return (bool) fPtr; }

   /// Share the attribute, potentially transforming this into a shared attribute.
   std::shared_ptr<ATTR> GetSharedPtr() {
      if (!IsShared())
         fPtr = std::make_shared<ATTR>(std::move(fAttr));
      return fPtr;
   }

   /// Construct a TDrawingAttrOrRef from an shared_ptr.
   TDrawingAttrOrRef(const std::shared_ptr<ATTR> &otherPtr): fPtr(otherPtr) {}

public:
   /// Construct a default, non-shared attribute. The default value gets read from the default style,
   /// given the attribute's name.
   TDrawingAttrOrRef(const std::string &name) {
      InitializeAttrFromString(name, TStyle::GetCurrent().GetAttribute(name), fAttr);
   }

   /// Construct a default, non-shared attribute. The default value gets read from the default style,
   /// given the attribute's name and arguments for the default attribute constructor, should no
   /// style entry be found.
   template <class... ARGS>
   TDrawingAttrOrRef(const std::string &name, ARGS... args): fAttr(args...) {
      InitializeAttrFromString(name, TStyle::GetCurrent().GetAttribute(name), fAttr);
   }

   /// Construct a *non-shared* attribute, copying the attribute's value.
   TDrawingAttrOrRef(TDrawingAttrOrRef &other): fAttr(other.Get()) {}

   /// Move an attribute.
   TDrawingAttrOrRef(TDrawingAttrOrRef &&other) = default;

   /// Create a shared attribute.
   TDrawingAttrOrRef Share() {
      return GetSharedPtr();
   }

   /// Get the const attribute, whether it's shared or not.
   const ATTR &Get() const {
      if (IsShared())
         return *fPtr;
      return fAttr;
   }

   /// Get the non-const attribute, whether it's shared or not.
   ATTR &Get() {
      if (IsShared())
         return *fPtr;
      return fAttr;
   }

   /// Convert to an ATTR (const).
   explicit operator const ATTR& () const{ return Get(); }
   /// Convert to an ATTR (non-const).
   explicit operator ATTR& () { return Get(); }

   /// Assign an ATTR.
   TDrawingAttrOrRef& operator=(const ATTR& attr) { Get() = attr; return *this; }
   /// Move-assign an ATTR.
   TDrawingAttrOrRef& operator=(ATTR&& attr) { Get() = std::move(attr); return *this; }
};

} // namespace Experimental
} // namespace ROOT

#endif // ROOT7_TDrawingAttrs
<commit_msg>TDrawingAttrOrRef copy ctor constness.<commit_after>/// \file ROOT/TDrawingAttrs.hxx
/// \ingroup Gpad ROOT7
/// \author Axel Naumann <axel@cern.ch>
/// \date 2017-09-26
/// \warning This is part of the ROOT 7 prototype! It will change without notice. It might trigger earthquakes. Feedback
/// is welcome!

/*************************************************************************
 * Copyright (C) 1995-2017, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

#ifndef ROOT7_TDrawingAttrs
#define ROOT7_TDrawingAttrs

#include <ROOT/TStyle.hxx>

#include <memory>
#include <string>
#include <vector>

namespace ROOT {
namespace Experimental {

class TColor;
class TStringAttr;

///\{
/// Initialize an attribute `val` from a string value.
///
///\param[in] name - the attribute name, for diagnostic purposes.
///\param[in] strval - the attribute value as a string.
///\param[out] val - the value to be initialized.
void InitializeAttrFromString(const std::string &name, const std::string &strval, int& val);
void InitializeAttrFromString(const std::string &name, const std::string &strval, long long& val);
void InitializeAttrFromString(const std::string &name, const std::string &strval, float& val);
void InitializeAttrFromString(const std::string &name, const std::string &strval, std::string& val);
///\}

/** \class ROOT::Experimental::TDrawingAttrOrRef
 A wrapper for a graphics attribute, for instance a `TColor`.
 The `TTopmostPad` keeps track of shared attributes used by multiple drawing options by means of
 `weak_ptr`s; `TDrawingAttrOrRef`s hold `shared_ptr`s to these.
 The reference into the table of the shared attributes is wrapped into the reference of the `TDrawingAttrOrRef`
 to make them type-safe (i.e. distinct for `TColor`, `long long` and `double`).
 */

template <class ATTR>
class TDrawingAttrOrRef {
private:
   std::shared_ptr<ATTR> fPtr; ///< The shared_ptr, shared with the relevant attribute table of `TTopmostPad`.
   ATTR fAttr; ///< The attribute value, used if `!fPtr`.

   /// Whether this attribute is shared (through `TTopmostPad`'s attribute table) with other `TDrawingAttrOrRef`
   /// objects.
   bool IsShared() const { return (bool) fPtr; }

   /// Share the attribute, potentially transforming this into a shared attribute.
   std::shared_ptr<ATTR> GetSharedPtr() {
      if (!IsShared())
         fPtr = std::make_shared<ATTR>(std::move(fAttr));
      return fPtr;
   }

   /// Construct a TDrawingAttrOrRef from an shared_ptr.
   TDrawingAttrOrRef(const std::shared_ptr<ATTR> &otherPtr): fPtr(otherPtr) {}

public:
   /// Construct a default, non-shared attribute. The default value gets read from the default style,
   /// given the attribute's name.
   TDrawingAttrOrRef(const std::string &name) {
      InitializeAttrFromString(name, TStyle::GetCurrent().GetAttribute(name), fAttr);
   }

   /// Construct a default, non-shared attribute. The default value gets read from the default style,
   /// given the attribute's name and arguments for the default attribute constructor, should no
   /// style entry be found.
   template <class... ARGS>
   TDrawingAttrOrRef(const std::string &name, ARGS... args): fAttr(args...) {
      InitializeAttrFromString(name, TStyle::GetCurrent().GetAttribute(name), fAttr);
   }

   /// Construct a *non-shared* attribute, copying the attribute's value.
   TDrawingAttrOrRef(const TDrawingAttrOrRef &other): fAttr(other.Get()) {}

   /// Move an attribute.
   TDrawingAttrOrRef(TDrawingAttrOrRef &&other) = default;

   /// Create a shared attribute.
   TDrawingAttrOrRef Share() {
      return GetSharedPtr();
   }

   /// Get the const attribute, whether it's shared or not.
   const ATTR &Get() const {
      if (IsShared())
         return *fPtr;
      return fAttr;
   }

   /// Get the non-const attribute, whether it's shared or not.
   ATTR &Get() {
      if (IsShared())
         return *fPtr;
      return fAttr;
   }

   /// Convert to an ATTR (const).
   explicit operator const ATTR& () const{ return Get(); }
   /// Convert to an ATTR (non-const).
   explicit operator ATTR& () { return Get(); }

   /// Assign an ATTR.
   TDrawingAttrOrRef& operator=(const ATTR& attr) { Get() = attr; return *this; }
   /// Move-assign an ATTR.
   TDrawingAttrOrRef& operator=(ATTR&& attr) { Get() = std::move(attr); return *this; }
};

} // namespace Experimental
} // namespace ROOT

#endif // ROOT7_TDrawingAttrs
<|endoftext|>


<commit_before>/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (info@qt.nokia.com)
**
**
** GNU Lesser General Public License Usage
**
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this file.
** Please review the following information to ensure the GNU Lesser General
** Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
** If you have questions regarding the use of this file, please contact
** Nokia at info@qt.nokia.com.
**
**************************************************************************/

#include "qmlprofilerengine.h"

#include "canvas/qdeclarativecanvas_p.h"
#include "canvas/qdeclarativecontext2d_p.h"
#include "canvas/qdeclarativetiledcanvas_p.h"
#include "codaqmlprofilerrunner.h"
#include "localqmlprofilerrunner.h"
#include "remotelinuxqmlprofilerrunner.h"
#include "qmlprofilerplugin.h"
#include "qmlprofilertool.h"

#include <analyzerbase/analyzermanager.h>
#include <coreplugin/icore.h>
#include <utils/qtcassert.h>
#include <coreplugin/helpmanager.h>
#include <qmlprojectmanager/qmlprojectrunconfiguration.h>
#include <projectexplorer/localapplicationruncontrol.h>
#include <projectexplorer/applicationrunconfiguration.h>
#include <qt4projectmanager/qt-s60/s60devicedebugruncontrol.h>
#include <qt4projectmanager/qt-s60/s60devicerunconfiguration.h>

#include <QtGui/QMainWindow>
#include <QtGui/QMessageBox>

using namespace Analyzer;
using namespace ProjectExplorer;

namespace QmlProfiler {
namespace Internal {

//
// QmlProfilerEnginePrivate
//

class QmlProfilerEngine::QmlProfilerEnginePrivate
{
public:
    QmlProfilerEnginePrivate(QmlProfilerEngine *qq) : q(qq), m_runner(0) {}
    ~QmlProfilerEnginePrivate() {}

    bool attach(const QString &address, uint port);
    static AbstractQmlProfilerRunner *createRunner(ProjectExplorer::RunConfiguration *runConfiguration,
                                                   QObject *parent);

    QmlProfilerEngine *q;

    //AnalyzerStartParameters m_params;
    AbstractQmlProfilerRunner *m_runner;
    bool m_running;
    bool m_fetchingData;
    bool m_delayedDelete;
};

AbstractQmlProfilerRunner *
QmlProfilerEngine::QmlProfilerEnginePrivate::createRunner(ProjectExplorer::RunConfiguration *runConfiguration,
                                                          QObject *parent)
{
    AbstractQmlProfilerRunner *runner = 0;
    if (QmlProjectManager::QmlProjectRunConfiguration *rc1 =
            qobject_cast<QmlProjectManager::QmlProjectRunConfiguration *>(runConfiguration)) {
        // This is a "plain" .qmlproject.
        LocalQmlProfilerRunner::Configuration conf;
        conf.executable = rc1->observerPath();
        conf.executableArguments = rc1->viewerArguments();
        conf.workingDirectory = rc1->workingDirectory();
        conf.environment = rc1->environment();
        conf.port = rc1->qmlDebugServerPort();
        runner = new LocalQmlProfilerRunner(conf, parent);
    } else if (LocalApplicationRunConfiguration *rc2 =
            qobject_cast<LocalApplicationRunConfiguration *>(runConfiguration)) {
        // FIXME: Check.
        LocalQmlProfilerRunner::Configuration conf;
        conf.executable = rc2->executable();
        conf.executableArguments = rc2->commandLineArguments();
        conf.workingDirectory = rc2->workingDirectory();
        conf.environment = rc2->environment();
        conf.port = rc2->qmlDebugServerPort();
        runner = new LocalQmlProfilerRunner(conf, parent);
    } else if (Qt4ProjectManager::S60DeviceRunConfiguration *s60Config =
            qobject_cast<Qt4ProjectManager::S60DeviceRunConfiguration*>(runConfiguration)) {
        runner = new CodaQmlProfilerRunner(s60Config, parent);
    } else if (RemoteLinux::RemoteLinuxRunConfiguration *rmConfig =
            qobject_cast<RemoteLinux::RemoteLinuxRunConfiguration *>(runConfiguration)) {
        runner = new RemoteLinuxQmlProfilerRunner(rmConfig, parent);
    } else {
        QTC_ASSERT(false, /**/);
    }
    return runner;
}

//
// QmlProfilerEngine
//

QmlProfilerEngine::QmlProfilerEngine(IAnalyzerTool *tool,
         ProjectExplorer::RunConfiguration *runConfiguration)
    : IAnalyzerEngine(tool, runConfiguration)
    , d(new QmlProfilerEnginePrivate(this))
{
    d->m_running = false;
    d->m_fetchingData = false;
    d->m_delayedDelete = false;
}

QmlProfilerEngine::~QmlProfilerEngine()
{
    if (d->m_running)
        stop();
    delete d;
}

void QmlProfilerEngine::start()
{
    QTC_ASSERT(!d->m_runner, return);
    d->m_runner = QmlProfilerEnginePrivate::createRunner(runConfiguration(), this);

    connect(d->m_runner, SIGNAL(stopped()), this, SLOT(stopped()));
    connect(d->m_runner, SIGNAL(appendMessage(QString,Utils::OutputFormat)),
            this, SLOT(logApplicationMessage(QString,Utils::OutputFormat)));

    d->m_runner->start();

    d->m_running = true;
    d->m_delayedDelete = false;

    AnalyzerManager::handleToolStarted();
}

void QmlProfilerEngine::stop()
{
    if (d->m_fetchingData) {
        if (d->m_running)
            d->m_delayedDelete = true;
        // will result in dataReceived() call
        emit stopRecording();
    } else {
        finishProcess();
    }
}

void QmlProfilerEngine::stopped()
{
    // user feedback
    if (d->m_running && d->m_fetchingData) {
        Core::ICore * const core = Core::ICore::instance();
        QMessageBox *killedWarning = new QMessageBox(core->mainWindow());
        killedWarning->setIcon(QMessageBox::Warning);
        killedWarning->setWindowTitle(tr("QML Profiler"));
        killedWarning->setText(tr("Application finished before loading profiled data.\n Please use the stop button instead."));
        killedWarning->setStandardButtons(QMessageBox::Ok);
        killedWarning->setDefaultButton(QMessageBox::Ok);
        killedWarning->setModal(false);
        killedWarning->show();
    }

    d->m_running = false;
    AnalyzerManager::stopTool(); // FIXME: Needed?
    emit finished();
}

void QmlProfilerEngine::setFetchingData(bool b)
{
    d->m_fetchingData = b;
}

void QmlProfilerEngine::dataReceived()
{
    if (d->m_delayedDelete)
        finishProcess();
    d->m_delayedDelete = false;
}

void QmlProfilerEngine::finishProcess()
{
    // user stop?
    if (d->m_running) {
        d->m_running = false;
        d->m_runner->stop();
        emit finished();
    }
}

void QmlProfilerEngine::filterApplicationMessage(const QString &msg)
{
    static const QString qddserver = QLatin1String("QDeclarativeDebugServer: ");
    static const QString cannotRetrieveDebuggingOutput = ProjectExplorer::ApplicationLauncher::msgWinCannotRetrieveDebuggingOutput();

    const int index = msg.indexOf(qddserver);
    if (index != -1) {
        QString status = msg;
        status.remove(0, index + qddserver.length()); // chop of 'QDeclarativeDebugServer: '

        static QString waitingForConnection = QLatin1String("Waiting for connection ");
        static QString unableToListen = QLatin1String("Unable to listen ");
        static QString debuggingNotEnabled = QLatin1String("Ignoring \"-qmljsdebugger=");
        static QString debuggingNotEnabled2 = QLatin1String("Ignoring\"-qmljsdebugger="); // There is (was?) a bug in one of the error strings - safest to handle both
        static QString connectionEstablished = QLatin1String("Connection established");

        QString errorMessage;
        if (status.startsWith(waitingForConnection)) {
            emit processRunning(d->m_runner->debugPort());
        } else if (status.startsWith(unableToListen)) {
            //: Error message shown after 'Could not connect ... debugger:"
            errorMessage = tr("The port seems to be in use.");
        } else if (status.startsWith(debuggingNotEnabled) || status.startsWith(debuggingNotEnabled2)) {
            //: Error message shown after 'Could not connect ... debugger:"
            errorMessage = tr("The application is not set up for QML/JS debugging.");
        } else if (status.startsWith(connectionEstablished)) {
            // nothing to do
        } else {
            qWarning() << "Unknown QDeclarativeDebugServer status message: " << status;
        }

        if (!errorMessage.isEmpty()) {
            Core::ICore * const core = Core::ICore::instance();
            QMessageBox *infoBox = new QMessageBox(core->mainWindow());
            infoBox->setIcon(QMessageBox::Critical);
            infoBox->setWindowTitle(tr("Qt Creator"));
            //: %1 is detailed error message
            infoBox->setText(tr("Could not connect to the in-process QML debugger:\n%1")
                             .arg(errorMessage));
            infoBox->setStandardButtons(QMessageBox::Ok | QMessageBox::Help);
            infoBox->setDefaultButton(QMessageBox::Ok);
            infoBox->setModal(true);

            connect(infoBox, SIGNAL(finished(int)),
                    this, SLOT(wrongSetupMessageBoxFinished(int)));

            infoBox->show();
        }
    } else if (msg.contains(cannotRetrieveDebuggingOutput)) {
        // we won't get debugging output, so just try to connect ...
        emit processRunning(d->m_runner->debugPort());
    }
}

void QmlProfilerEngine::logApplicationMessage(const QString &msg, Utils::OutputFormat format)
{
    emit outputReceived(msg, format);

    filterApplicationMessage(msg);
}

void QmlProfilerEngine::wrongSetupMessageBoxFinished(int button)
{
    if (button == QMessageBox::Help) {
        Core::HelpManager *helpManager = Core::HelpManager::instance();
        helpManager->handleHelpRequest("qthelp://com.nokia.qtcreator/doc/creator-qml-performance-monitor.html");
    }
}

} // namespace Internal
} // namespace QmlProfiler
<commit_msg>QmlProfiler: show warning when no observer is present<commit_after>/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (info@qt.nokia.com)
**
**
** GNU Lesser General Public License Usage
**
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this file.
** Please review the following information to ensure the GNU Lesser General
** Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
** If you have questions regarding the use of this file, please contact
** Nokia at info@qt.nokia.com.
**
**************************************************************************/

#include "qmlprofilerengine.h"

#include "canvas/qdeclarativecanvas_p.h"
#include "canvas/qdeclarativecontext2d_p.h"
#include "canvas/qdeclarativetiledcanvas_p.h"
#include "codaqmlprofilerrunner.h"
#include "localqmlprofilerrunner.h"
#include "remotelinuxqmlprofilerrunner.h"
#include "qmlprofilerplugin.h"
#include "qmlprofilertool.h"

#include <analyzerbase/analyzermanager.h>
#include <coreplugin/icore.h>
#include <utils/qtcassert.h>
#include <coreplugin/helpmanager.h>
#include <qmlprojectmanager/qmlprojectrunconfiguration.h>
#include <qmlprojectmanager/qmlprojectplugin.h>
#include <projectexplorer/localapplicationruncontrol.h>
#include <projectexplorer/applicationrunconfiguration.h>
#include <qt4projectmanager/qt-s60/s60devicedebugruncontrol.h>
#include <qt4projectmanager/qt-s60/s60devicerunconfiguration.h>

#include <QtGui/QMainWindow>
#include <QtGui/QMessageBox>

using namespace Analyzer;
using namespace ProjectExplorer;

namespace QmlProfiler {
namespace Internal {

//
// QmlProfilerEnginePrivate
//

class QmlProfilerEngine::QmlProfilerEnginePrivate
{
public:
    QmlProfilerEnginePrivate(QmlProfilerEngine *qq) : q(qq), m_runner(0) {}
    ~QmlProfilerEnginePrivate() {}

    bool attach(const QString &address, uint port);
    static AbstractQmlProfilerRunner *createRunner(ProjectExplorer::RunConfiguration *runConfiguration,
                                                   QObject *parent);

    QmlProfilerEngine *q;

    //AnalyzerStartParameters m_params;
    AbstractQmlProfilerRunner *m_runner;
    bool m_running;
    bool m_fetchingData;
    bool m_delayedDelete;
};

AbstractQmlProfilerRunner *
QmlProfilerEngine::QmlProfilerEnginePrivate::createRunner(ProjectExplorer::RunConfiguration *runConfiguration,
                                                          QObject *parent)
{
    AbstractQmlProfilerRunner *runner = 0;
    if (QmlProjectManager::QmlProjectRunConfiguration *rc1 =
            qobject_cast<QmlProjectManager::QmlProjectRunConfiguration *>(runConfiguration)) {
        // This is a "plain" .qmlproject.
        LocalQmlProfilerRunner::Configuration conf;
        conf.executable = rc1->observerPath();
        conf.executableArguments = rc1->viewerArguments();
        conf.workingDirectory = rc1->workingDirectory();
        conf.environment = rc1->environment();
        conf.port = rc1->qmlDebugServerPort();
        runner = new LocalQmlProfilerRunner(conf, parent);
    } else if (LocalApplicationRunConfiguration *rc2 =
            qobject_cast<LocalApplicationRunConfiguration *>(runConfiguration)) {
        // FIXME: Check.
        LocalQmlProfilerRunner::Configuration conf;
        conf.executable = rc2->executable();
        conf.executableArguments = rc2->commandLineArguments();
        conf.workingDirectory = rc2->workingDirectory();
        conf.environment = rc2->environment();
        conf.port = rc2->qmlDebugServerPort();
        runner = new LocalQmlProfilerRunner(conf, parent);
    } else if (Qt4ProjectManager::S60DeviceRunConfiguration *s60Config =
            qobject_cast<Qt4ProjectManager::S60DeviceRunConfiguration*>(runConfiguration)) {
        runner = new CodaQmlProfilerRunner(s60Config, parent);
    } else if (RemoteLinux::RemoteLinuxRunConfiguration *rmConfig =
            qobject_cast<RemoteLinux::RemoteLinuxRunConfiguration *>(runConfiguration)) {
        runner = new RemoteLinuxQmlProfilerRunner(rmConfig, parent);
    } else {
        QTC_ASSERT(false, /**/);
    }
    return runner;
}

//
// QmlProfilerEngine
//

QmlProfilerEngine::QmlProfilerEngine(IAnalyzerTool *tool,
         ProjectExplorer::RunConfiguration *runConfiguration)
    : IAnalyzerEngine(tool, runConfiguration)
    , d(new QmlProfilerEnginePrivate(this))
{
    d->m_running = false;
    d->m_fetchingData = false;
    d->m_delayedDelete = false;
}

QmlProfilerEngine::~QmlProfilerEngine()
{
    if (d->m_running)
        stop();
    delete d;
}

void QmlProfilerEngine::start()
{
    QTC_ASSERT(!d->m_runner, return);

    if (QmlProjectManager::QmlProjectRunConfiguration *rc =
            qobject_cast<QmlProjectManager::QmlProjectRunConfiguration *>(runConfiguration())) {
        if (rc->observerPath().isEmpty()) {
            QmlProjectManager::QmlProjectPlugin::showQmlObserverToolWarning();
            return;
        }
    }

    d->m_runner = QmlProfilerEnginePrivate::createRunner(runConfiguration(), this);

    connect(d->m_runner, SIGNAL(stopped()), this, SLOT(stopped()));
    connect(d->m_runner, SIGNAL(appendMessage(QString,Utils::OutputFormat)),
            this, SLOT(logApplicationMessage(QString,Utils::OutputFormat)));

    d->m_runner->start();

    d->m_running = true;
    d->m_delayedDelete = false;

    AnalyzerManager::handleToolStarted();
}

void QmlProfilerEngine::stop()
{
    if (d->m_fetchingData) {
        if (d->m_running)
            d->m_delayedDelete = true;
        // will result in dataReceived() call
        emit stopRecording();
    } else {
        finishProcess();
    }
}

void QmlProfilerEngine::stopped()
{
    // user feedback
    if (d->m_running && d->m_fetchingData) {
        Core::ICore * const core = Core::ICore::instance();
        QMessageBox *killedWarning = new QMessageBox(core->mainWindow());
        killedWarning->setIcon(QMessageBox::Warning);
        killedWarning->setWindowTitle(tr("QML Profiler"));
        killedWarning->setText(tr("Application finished before loading profiled data.\n Please use the stop button instead."));
        killedWarning->setStandardButtons(QMessageBox::Ok);
        killedWarning->setDefaultButton(QMessageBox::Ok);
        killedWarning->setModal(false);
        killedWarning->show();
    }

    d->m_running = false;
    AnalyzerManager::stopTool(); // FIXME: Needed?
    emit finished();
}

void QmlProfilerEngine::setFetchingData(bool b)
{
    d->m_fetchingData = b;
}

void QmlProfilerEngine::dataReceived()
{
    if (d->m_delayedDelete)
        finishProcess();
    d->m_delayedDelete = false;
}

void QmlProfilerEngine::finishProcess()
{
    // user stop?
    if (d->m_running) {
        d->m_running = false;
        d->m_runner->stop();
        emit finished();
    }
}

void QmlProfilerEngine::filterApplicationMessage(const QString &msg)
{
    static const QString qddserver = QLatin1String("QDeclarativeDebugServer: ");
    static const QString cannotRetrieveDebuggingOutput = ProjectExplorer::ApplicationLauncher::msgWinCannotRetrieveDebuggingOutput();

    const int index = msg.indexOf(qddserver);
    if (index != -1) {
        QString status = msg;
        status.remove(0, index + qddserver.length()); // chop of 'QDeclarativeDebugServer: '

        static QString waitingForConnection = QLatin1String("Waiting for connection ");
        static QString unableToListen = QLatin1String("Unable to listen ");
        static QString debuggingNotEnabled = QLatin1String("Ignoring \"-qmljsdebugger=");
        static QString debuggingNotEnabled2 = QLatin1String("Ignoring\"-qmljsdebugger="); // There is (was?) a bug in one of the error strings - safest to handle both
        static QString connectionEstablished = QLatin1String("Connection established");

        QString errorMessage;
        if (status.startsWith(waitingForConnection)) {
            emit processRunning(d->m_runner->debugPort());
        } else if (status.startsWith(unableToListen)) {
            //: Error message shown after 'Could not connect ... debugger:"
            errorMessage = tr("The port seems to be in use.");
        } else if (status.startsWith(debuggingNotEnabled) || status.startsWith(debuggingNotEnabled2)) {
            //: Error message shown after 'Could not connect ... debugger:"
            errorMessage = tr("The application is not set up for QML/JS debugging.");
        } else if (status.startsWith(connectionEstablished)) {
            // nothing to do
        } else {
            qWarning() << "Unknown QDeclarativeDebugServer status message: " << status;
        }

        if (!errorMessage.isEmpty()) {
            Core::ICore * const core = Core::ICore::instance();
            QMessageBox *infoBox = new QMessageBox(core->mainWindow());
            infoBox->setIcon(QMessageBox::Critical);
            infoBox->setWindowTitle(tr("Qt Creator"));
            //: %1 is detailed error message
            infoBox->setText(tr("Could not connect to the in-process QML debugger:\n%1")
                             .arg(errorMessage));
            infoBox->setStandardButtons(QMessageBox::Ok | QMessageBox::Help);
            infoBox->setDefaultButton(QMessageBox::Ok);
            infoBox->setModal(true);

            connect(infoBox, SIGNAL(finished(int)),
                    this, SLOT(wrongSetupMessageBoxFinished(int)));

            infoBox->show();
        }
    } else if (msg.contains(cannotRetrieveDebuggingOutput)) {
        // we won't get debugging output, so just try to connect ...
        emit processRunning(d->m_runner->debugPort());
    }
}

void QmlProfilerEngine::logApplicationMessage(const QString &msg, Utils::OutputFormat format)
{
    emit outputReceived(msg, format);

    filterApplicationMessage(msg);
}

void QmlProfilerEngine::wrongSetupMessageBoxFinished(int button)
{
    if (button == QMessageBox::Help) {
        Core::HelpManager *helpManager = Core::HelpManager::instance();
        helpManager->handleHelpRequest("qthelp://com.nokia.qtcreator/doc/creator-qml-performance-monitor.html");
    }
}

} // namespace Internal
} // namespace QmlProfiler
<|endoftext|>


<commit_before>//Copyright (c) 2017 Ultimaker B.V.
//CuraEngine is released under the terms of the AGPLv3 or higher.

#include "sliceDataStorage.h"

#include "FffProcessor.h" //To create a mesh group with if none is provided.
#include "infill/SubDivCube.h" // For the destructor

namespace cura
{

Polygons& SliceLayerPart::getOwnInfillArea()
{
    return const_cast<Polygons&>(const_cast<const SliceLayerPart*>(this)->getOwnInfillArea());
}

const Polygons& SliceLayerPart::getOwnInfillArea() const
{
    if (infill_area_own)
    {
        return *infill_area_own;
    }
    else
    {
        return infill_area;
    }
}

Polygons SliceLayer::getOutlines(bool external_polys_only) const
{
    Polygons ret;
    getOutlines(ret, external_polys_only);
    return ret;
}

void SliceLayer::getOutlines(Polygons& result, bool external_polys_only) const
{
    for (const SliceLayerPart& part : parts)
    {
        if (external_polys_only)
        {
            result.add(part.outline.outerPolygon());
        }
        else
        {
            result.add(part.print_outline);
        }
    }
}

Polygons SliceLayer::getSecondOrInnermostWalls() const
{
    Polygons ret;
    getSecondOrInnermostWalls(ret);
    return ret;
}

void SliceLayer::getSecondOrInnermostWalls(Polygons& layer_walls) const
{
    for (const SliceLayerPart& part : parts)
    {
        // we want the 2nd inner walls
        if (part.insets.size() >= 2) {
            layer_walls.add(part.insets[1]);
            continue;
        }
        // but we'll also take the inner wall if the 2nd doesn't exist
        if (part.insets.size() == 1) {
            layer_walls.add(part.insets[0]);
            continue;
        }
        // offset_from_outlines was so large that it completely destroyed our isle,
        // so we'll just use the regular outline
        layer_walls.add(part.outline);
        continue;
    }
}

SliceMeshStorage::~SliceMeshStorage()
{
    if (base_subdiv_cube)
    {
        delete base_subdiv_cube;
    }
}

bool SliceMeshStorage::getExtruderIsUsed(int extruder_nr) const
{
    if (getSettingBoolean("magic_spiralize"))
    {
        if (getSettingAsIndex("wall_0_extruder_nr") == extruder_nr)
        {
            return true;
        }
        else
        {
            return false;
        }
    }
    if (getSettingAsCount("wall_line_count") > 0 && getSettingAsIndex("wall_0_extruder_nr") == extruder_nr)
    {
        return true;
    }
    if ((getSettingAsCount("wall_line_count") > 1 || getSettingBoolean("alternate_extra_perimeter") || getSettingBoolean("fill_perimeter_gaps"))
        && getSettingAsIndex("wall_x_extruder_nr") == extruder_nr)
    {
        return true;
    }
    if (getSettingInMicrons("infill_line_distance") > 0 && getSettingAsIndex("infill_extruder_nr") == extruder_nr)
    {
        return true;
    }
    if ((getSettingAsCount("top_layers") > 0 || getSettingAsCount("bottom_layers")) && getSettingAsIndex("top_bottom_extruder_nr") == extruder_nr)
    {
        return true;
    }
    return false;
}

bool SliceMeshStorage::getExtruderIsUsed(int extruder_nr, int layer_nr) const
{
    if (layer_nr < 0 || layer_nr >= static_cast<int>(layers.size()))
    {
        return false;
    }
    if (getSettingBoolean("anti_overhang_mesh")
        || getSettingBoolean("support_mesh"))
    { // object is not printed as object, but as support.
        return false;
    }
    const SliceLayer& layer = layers[layer_nr];
    if (getSettingAsCount("wall_line_count") > 0 && getSettingAsIndex("wall_0_extruder_nr") == extruder_nr)
    {
        for (const SliceLayerPart& part : layer.parts)
        {
            if (part.insets.size() > 0 && part.insets[0].size() > 0)
            {
                return true;
            }
        }
    }
    if (getSettingAsFillPerimeterGapMode("fill_perimeter_gaps") != FillPerimeterGapMode::NOWHERE
        && (getSettingAsCount("wall_line_count") > 0 || getSettingAsCount("skin_outline_count") > 0)
        && getSettingAsIndex("wall_0_extruder_nr") == extruder_nr)
    {
        for (const SliceLayerPart& part : layer.parts)
        {
            if (part.perimeter_gaps.size() > 0)
            {
                return true;
            }
            for (const SkinPart& skin_part : part.skin_parts)
            {
                if (skin_part.perimeter_gaps.size() > 0)
                {
                    return true;
                }
            }
        }
    }
    if ((getSettingAsCount("wall_line_count") > 1 || getSettingBoolean("alternate_extra_perimeter")) && getSettingAsIndex("wall_x_extruder_nr") == extruder_nr)
    {
        for (const SliceLayerPart& part : layer.parts)
        {
            if (part.insets.size() > 1 && part.insets[1].size() > 0)
            {
                return true;
            }
        }
    }
    if (getSettingInMicrons("infill_line_distance") > 0 && getSettingAsIndex("infill_extruder_nr") == extruder_nr)
    {
        for (const SliceLayerPart& part : layer.parts)
        {
            if (part.getOwnInfillArea().size() > 0)
            {
                return true;
            }
        }
    }
    if (getSettingAsIndex("top_bottom_extruder_nr") == extruder_nr)
    {
        for (const SliceLayerPart& part : layer.parts)
        {
            if (!part.skin_parts.empty())
            {
                return true;
            }
        }
    }
    return false;
}

std::vector<RetractionConfig> SliceDataStorage::initializeRetractionConfigs()
{
    std::vector<RetractionConfig> ret;
    ret.resize(meshgroup->getExtruderCount()); // initializes with constructor RetractionConfig()
    return ret;
}

SliceDataStorage::SliceDataStorage(MeshGroup* meshgroup) : SettingsMessenger(meshgroup),
    meshgroup(meshgroup != nullptr ? meshgroup : new MeshGroup(FffProcessor::getInstance())), //If no mesh group is provided, we roll our own.
    print_layer_count(0),
    retraction_config_per_extruder(initializeRetractionConfigs()),
    extruder_switch_retraction_config_per_extruder(initializeRetractionConfigs()),
    max_print_height_second_to_last_extruder(-1),
    primeTower(*this)
{
}

Polygons SliceDataStorage::getLayerOutlines(int layer_nr, bool include_helper_parts, bool external_polys_only) const
{
    if (layer_nr < 0 && layer_nr < -Raft::getFillerLayerCount(*this))
    { // when processing raft
        if (include_helper_parts)
        {
            if (external_polys_only)
            {
                std::vector<PolygonsPart> parts = raftOutline.splitIntoParts();
                Polygons result;
                for (PolygonsPart& part : parts)
                {
                    result.add(part.outerPolygon());
                }
                return result;
            }
            else
            {
                return raftOutline;
            }
        }
        else
        {
            return Polygons();
        }
    }
    else
    {
        Polygons total;
        if (layer_nr >= 0)
        {
            for (const SliceMeshStorage& mesh : meshes)
            {
                if (mesh.getSettingBoolean("infill_mesh") || mesh.getSettingBoolean("anti_overhang_mesh"))
                {
                    continue;
                }
                const SliceLayer& layer = mesh.layers[layer_nr];
                layer.getOutlines(total, external_polys_only);
                if (mesh.getSettingAsSurfaceMode("magic_mesh_surface_mode") != ESurfaceMode::NORMAL)
                {
                    total = total.unionPolygons(layer.openPolyLines.offsetPolyLine(100));
                }
            }
        }
        if (include_helper_parts)
        {
            if (support.generated)
            {
                total.add(support.supportLayers[std::max(0, layer_nr)].supportAreas);
                total.add(support.supportLayers[std::max(0, layer_nr)].support_bottom);
                total.add(support.supportLayers[std::max(0, layer_nr)].support_roof);
            }
            if (primeTower.enabled)
            {
                total.add(primeTower.ground_poly);
            }
        }
        return total;
    }
}

Polygons SliceDataStorage::getLayerSecondOrInnermostWalls(int layer_nr, bool include_helper_parts) const
{
    if (layer_nr < 0 && layer_nr < -Raft::getFillerLayerCount(*this))
    { // when processing raft
        if (include_helper_parts)
        {
            return raftOutline;
        }
        else
        {
            return Polygons();
        }
    }
    else
    {
        Polygons total;
        if (layer_nr >= 0)
        {
            for (const SliceMeshStorage& mesh : meshes)
            {
                const SliceLayer& layer = mesh.layers[layer_nr];
                layer.getSecondOrInnermostWalls(total);
                if (mesh.getSettingAsSurfaceMode("magic_mesh_surface_mode") != ESurfaceMode::NORMAL)
                {
                    total = total.unionPolygons(layer.openPolyLines.offsetPolyLine(100));
                }
            }
        }
        if (include_helper_parts)
        {
            if (support.generated)
            {
                total.add(support.supportLayers[std::max(0, layer_nr)].supportAreas);
                total.add(support.supportLayers[std::max(0, layer_nr)].support_bottom);
                total.add(support.supportLayers[std::max(0, layer_nr)].support_roof);
            }
            if (primeTower.enabled)
            {
                total.add(primeTower.ground_poly);
            }
        }
        return total;
    }

}

std::vector<bool> SliceDataStorage::getExtrudersUsed() const
{

    std::vector<bool> ret;
    ret.resize(meshgroup->getExtruderCount(), false);

    if (getSettingAsPlatformAdhesion("adhesion_type") != EPlatformAdhesion::NONE)
    {
        ret[getSettingAsIndex("adhesion_extruder_nr")] = true;
        { // process brim/skirt
            for (int extr_nr = 0; extr_nr < meshgroup->getExtruderCount(); extr_nr++)
            {
                if (skirt_brim[extr_nr].size() > 0)
                {
                    ret[extr_nr] = true;
                    continue;
                }
            }
        }
    }

    // TODO: ooze shield, draft shield ..?

    // support
    // support is presupposed to be present...
    for (const SliceMeshStorage& mesh : meshes)
    {
        if (mesh.getSettingBoolean("support_enable") || mesh.getSettingBoolean("support_mesh"))
        {
            ret[getSettingAsIndex("support_extruder_nr_layer_0")] = true;
            ret[getSettingAsIndex("support_infill_extruder_nr")] = true;
            if (getSettingBoolean("support_roof_enable"))
            {
                ret[getSettingAsIndex("support_roof_extruder_nr")] = true;
            }
            if (getSettingBoolean("support_bottom_enable"))
            {
                ret[getSettingAsIndex("support_bottom_extruder_nr")] = true;
            }
        }
    }

    // all meshes are presupposed to actually have content
    for (const SliceMeshStorage& mesh : meshes)
    {
        for (unsigned int extruder_nr = 0; extruder_nr <= ret.size(); extruder_nr++)
        {
            ret[extruder_nr] = ret[extruder_nr] || mesh.getExtruderIsUsed(extruder_nr);
        }
    }
    return ret;
}

std::vector<bool> SliceDataStorage::getExtrudersUsed(int layer_nr) const
{

    std::vector<bool> ret;
    ret.resize(meshgroup->getExtruderCount(), false);

    bool include_adhesion = true;
    bool include_helper_parts = true;
    bool include_models = true;
    if (layer_nr < 0)
    {
        include_models = false;
        if (layer_nr < -Raft::getFillerLayerCount(*this))
        {
            include_helper_parts = false;
        }
        else
        {
            layer_nr = 0; // because the helper parts are copied from the initial layer in the filler layer
            include_adhesion = false;
        }
    }
    else if (layer_nr > 0 || getSettingAsPlatformAdhesion("adhesion_type") == EPlatformAdhesion::RAFT)
    { // only include adhesion only for layers where platform adhesion actually occurs
        // i.e. layers < 0 are for raft, layer 0 is for brim/skirt
        include_adhesion = false;
    }
    if (include_adhesion)
    {
        ret[getSettingAsIndex("adhesion_extruder_nr")] = true;
        { // process brim/skirt
            for (int extr_nr = 0; extr_nr < meshgroup->getExtruderCount(); extr_nr++)
            {
                if (skirt_brim[extr_nr].size() > 0)
                {
                    ret[extr_nr] = true;
                    continue;
                }
            }
        }
    }

    // TODO: ooze shield, draft shield ..?

    if (include_helper_parts)
    {
        // support
        if (layer_nr < int(support.supportLayers.size()))
        {
            const SupportLayer& support_layer = support.supportLayers[layer_nr];
            if (layer_nr == 0)
            {
                if (!support_layer.supportAreas.empty())
                {
                    ret[getSettingAsIndex("support_extruder_nr_layer_0")] = true;
                }
            }
            else
            {
                if (!support_layer.supportAreas.empty())
                {
                    ret[getSettingAsIndex("support_infill_extruder_nr")] = true;
                }
            }
            if (!support_layer.support_bottom.empty())
            {
                ret[getSettingAsIndex("support_bottom_extruder_nr")] = true;
            }
            if (!support_layer.support_roof.empty())
            {
                ret[getSettingAsIndex("support_roof_extruder_nr")] = true;
            }
        }
    }

    if (include_models)
    {
        for (const SliceMeshStorage& mesh : meshes)
        {
            for (unsigned int extruder_nr = 0; extruder_nr <= ret.size(); extruder_nr++)
            {
                ret[extruder_nr] = ret[extruder_nr] || mesh.getExtruderIsUsed(extruder_nr, layer_nr);
            }
        }
    }
    return ret;
}

} // namespace cura<commit_msg>Explicitly compare bottom_layers with 0<commit_after>//Copyright (c) 2017 Ultimaker B.V.
//CuraEngine is released under the terms of the AGPLv3 or higher.

#include "sliceDataStorage.h"

#include "FffProcessor.h" //To create a mesh group with if none is provided.
#include "infill/SubDivCube.h" // For the destructor

namespace cura
{

Polygons& SliceLayerPart::getOwnInfillArea()
{
    return const_cast<Polygons&>(const_cast<const SliceLayerPart*>(this)->getOwnInfillArea());
}

const Polygons& SliceLayerPart::getOwnInfillArea() const
{
    if (infill_area_own)
    {
        return *infill_area_own;
    }
    else
    {
        return infill_area;
    }
}

Polygons SliceLayer::getOutlines(bool external_polys_only) const
{
    Polygons ret;
    getOutlines(ret, external_polys_only);
    return ret;
}

void SliceLayer::getOutlines(Polygons& result, bool external_polys_only) const
{
    for (const SliceLayerPart& part : parts)
    {
        if (external_polys_only)
        {
            result.add(part.outline.outerPolygon());
        }
        else
        {
            result.add(part.print_outline);
        }
    }
}

Polygons SliceLayer::getSecondOrInnermostWalls() const
{
    Polygons ret;
    getSecondOrInnermostWalls(ret);
    return ret;
}

void SliceLayer::getSecondOrInnermostWalls(Polygons& layer_walls) const
{
    for (const SliceLayerPart& part : parts)
    {
        // we want the 2nd inner walls
        if (part.insets.size() >= 2) {
            layer_walls.add(part.insets[1]);
            continue;
        }
        // but we'll also take the inner wall if the 2nd doesn't exist
        if (part.insets.size() == 1) {
            layer_walls.add(part.insets[0]);
            continue;
        }
        // offset_from_outlines was so large that it completely destroyed our isle,
        // so we'll just use the regular outline
        layer_walls.add(part.outline);
        continue;
    }
}

SliceMeshStorage::~SliceMeshStorage()
{
    if (base_subdiv_cube)
    {
        delete base_subdiv_cube;
    }
}

bool SliceMeshStorage::getExtruderIsUsed(int extruder_nr) const
{
    if (getSettingBoolean("magic_spiralize"))
    {
        if (getSettingAsIndex("wall_0_extruder_nr") == extruder_nr)
        {
            return true;
        }
        else
        {
            return false;
        }
    }
    if (getSettingAsCount("wall_line_count") > 0 && getSettingAsIndex("wall_0_extruder_nr") == extruder_nr)
    {
        return true;
    }
    if ((getSettingAsCount("wall_line_count") > 1 || getSettingBoolean("alternate_extra_perimeter") || getSettingBoolean("fill_perimeter_gaps"))
        && getSettingAsIndex("wall_x_extruder_nr") == extruder_nr)
    {
        return true;
    }
    if (getSettingInMicrons("infill_line_distance") > 0 && getSettingAsIndex("infill_extruder_nr") == extruder_nr)
    {
        return true;
    }
    if ((getSettingAsCount("top_layers") > 0 || getSettingAsCount("bottom_layers") > 0) && getSettingAsIndex("top_bottom_extruder_nr") == extruder_nr)
    {
        return true;
    }
    return false;
}

bool SliceMeshStorage::getExtruderIsUsed(int extruder_nr, int layer_nr) const
{
    if (layer_nr < 0 || layer_nr >= static_cast<int>(layers.size()))
    {
        return false;
    }
    if (getSettingBoolean("anti_overhang_mesh")
        || getSettingBoolean("support_mesh"))
    { // object is not printed as object, but as support.
        return false;
    }
    const SliceLayer& layer = layers[layer_nr];
    if (getSettingAsCount("wall_line_count") > 0 && getSettingAsIndex("wall_0_extruder_nr") == extruder_nr)
    {
        for (const SliceLayerPart& part : layer.parts)
        {
            if (part.insets.size() > 0 && part.insets[0].size() > 0)
            {
                return true;
            }
        }
    }
    if (getSettingAsFillPerimeterGapMode("fill_perimeter_gaps") != FillPerimeterGapMode::NOWHERE
        && (getSettingAsCount("wall_line_count") > 0 || getSettingAsCount("skin_outline_count") > 0)
        && getSettingAsIndex("wall_0_extruder_nr") == extruder_nr)
    {
        for (const SliceLayerPart& part : layer.parts)
        {
            if (part.perimeter_gaps.size() > 0)
            {
                return true;
            }
            for (const SkinPart& skin_part : part.skin_parts)
            {
                if (skin_part.perimeter_gaps.size() > 0)
                {
                    return true;
                }
            }
        }
    }
    if ((getSettingAsCount("wall_line_count") > 1 || getSettingBoolean("alternate_extra_perimeter")) && getSettingAsIndex("wall_x_extruder_nr") == extruder_nr)
    {
        for (const SliceLayerPart& part : layer.parts)
        {
            if (part.insets.size() > 1 && part.insets[1].size() > 0)
            {
                return true;
            }
        }
    }
    if (getSettingInMicrons("infill_line_distance") > 0 && getSettingAsIndex("infill_extruder_nr") == extruder_nr)
    {
        for (const SliceLayerPart& part : layer.parts)
        {
            if (part.getOwnInfillArea().size() > 0)
            {
                return true;
            }
        }
    }
    if (getSettingAsIndex("top_bottom_extruder_nr") == extruder_nr)
    {
        for (const SliceLayerPart& part : layer.parts)
        {
            if (!part.skin_parts.empty())
            {
                return true;
            }
        }
    }
    return false;
}

std::vector<RetractionConfig> SliceDataStorage::initializeRetractionConfigs()
{
    std::vector<RetractionConfig> ret;
    ret.resize(meshgroup->getExtruderCount()); // initializes with constructor RetractionConfig()
    return ret;
}

SliceDataStorage::SliceDataStorage(MeshGroup* meshgroup) : SettingsMessenger(meshgroup),
    meshgroup(meshgroup != nullptr ? meshgroup : new MeshGroup(FffProcessor::getInstance())), //If no mesh group is provided, we roll our own.
    print_layer_count(0),
    retraction_config_per_extruder(initializeRetractionConfigs()),
    extruder_switch_retraction_config_per_extruder(initializeRetractionConfigs()),
    max_print_height_second_to_last_extruder(-1),
    primeTower(*this)
{
}

Polygons SliceDataStorage::getLayerOutlines(int layer_nr, bool include_helper_parts, bool external_polys_only) const
{
    if (layer_nr < 0 && layer_nr < -Raft::getFillerLayerCount(*this))
    { // when processing raft
        if (include_helper_parts)
        {
            if (external_polys_only)
            {
                std::vector<PolygonsPart> parts = raftOutline.splitIntoParts();
                Polygons result;
                for (PolygonsPart& part : parts)
                {
                    result.add(part.outerPolygon());
                }
                return result;
            }
            else
            {
                return raftOutline;
            }
        }
        else
        {
            return Polygons();
        }
    }
    else
    {
        Polygons total;
        if (layer_nr >= 0)
        {
            for (const SliceMeshStorage& mesh : meshes)
            {
                if (mesh.getSettingBoolean("infill_mesh") || mesh.getSettingBoolean("anti_overhang_mesh"))
                {
                    continue;
                }
                const SliceLayer& layer = mesh.layers[layer_nr];
                layer.getOutlines(total, external_polys_only);
                if (mesh.getSettingAsSurfaceMode("magic_mesh_surface_mode") != ESurfaceMode::NORMAL)
                {
                    total = total.unionPolygons(layer.openPolyLines.offsetPolyLine(100));
                }
            }
        }
        if (include_helper_parts)
        {
            if (support.generated)
            {
                total.add(support.supportLayers[std::max(0, layer_nr)].supportAreas);
                total.add(support.supportLayers[std::max(0, layer_nr)].support_bottom);
                total.add(support.supportLayers[std::max(0, layer_nr)].support_roof);
            }
            if (primeTower.enabled)
            {
                total.add(primeTower.ground_poly);
            }
        }
        return total;
    }
}

Polygons SliceDataStorage::getLayerSecondOrInnermostWalls(int layer_nr, bool include_helper_parts) const
{
    if (layer_nr < 0 && layer_nr < -Raft::getFillerLayerCount(*this))
    { // when processing raft
        if (include_helper_parts)
        {
            return raftOutline;
        }
        else
        {
            return Polygons();
        }
    }
    else
    {
        Polygons total;
        if (layer_nr >= 0)
        {
            for (const SliceMeshStorage& mesh : meshes)
            {
                const SliceLayer& layer = mesh.layers[layer_nr];
                layer.getSecondOrInnermostWalls(total);
                if (mesh.getSettingAsSurfaceMode("magic_mesh_surface_mode") != ESurfaceMode::NORMAL)
                {
                    total = total.unionPolygons(layer.openPolyLines.offsetPolyLine(100));
                }
            }
        }
        if (include_helper_parts)
        {
            if (support.generated)
            {
                total.add(support.supportLayers[std::max(0, layer_nr)].supportAreas);
                total.add(support.supportLayers[std::max(0, layer_nr)].support_bottom);
                total.add(support.supportLayers[std::max(0, layer_nr)].support_roof);
            }
            if (primeTower.enabled)
            {
                total.add(primeTower.ground_poly);
            }
        }
        return total;
    }

}

std::vector<bool> SliceDataStorage::getExtrudersUsed() const
{

    std::vector<bool> ret;
    ret.resize(meshgroup->getExtruderCount(), false);

    if (getSettingAsPlatformAdhesion("adhesion_type") != EPlatformAdhesion::NONE)
    {
        ret[getSettingAsIndex("adhesion_extruder_nr")] = true;
        { // process brim/skirt
            for (int extr_nr = 0; extr_nr < meshgroup->getExtruderCount(); extr_nr++)
            {
                if (skirt_brim[extr_nr].size() > 0)
                {
                    ret[extr_nr] = true;
                    continue;
                }
            }
        }
    }

    // TODO: ooze shield, draft shield ..?

    // support
    // support is presupposed to be present...
    for (const SliceMeshStorage& mesh : meshes)
    {
        if (mesh.getSettingBoolean("support_enable") || mesh.getSettingBoolean("support_mesh"))
        {
            ret[getSettingAsIndex("support_extruder_nr_layer_0")] = true;
            ret[getSettingAsIndex("support_infill_extruder_nr")] = true;
            if (getSettingBoolean("support_roof_enable"))
            {
                ret[getSettingAsIndex("support_roof_extruder_nr")] = true;
            }
            if (getSettingBoolean("support_bottom_enable"))
            {
                ret[getSettingAsIndex("support_bottom_extruder_nr")] = true;
            }
        }
    }

    // all meshes are presupposed to actually have content
    for (const SliceMeshStorage& mesh : meshes)
    {
        for (unsigned int extruder_nr = 0; extruder_nr <= ret.size(); extruder_nr++)
        {
            ret[extruder_nr] = ret[extruder_nr] || mesh.getExtruderIsUsed(extruder_nr);
        }
    }
    return ret;
}

std::vector<bool> SliceDataStorage::getExtrudersUsed(int layer_nr) const
{

    std::vector<bool> ret;
    ret.resize(meshgroup->getExtruderCount(), false);

    bool include_adhesion = true;
    bool include_helper_parts = true;
    bool include_models = true;
    if (layer_nr < 0)
    {
        include_models = false;
        if (layer_nr < -Raft::getFillerLayerCount(*this))
        {
            include_helper_parts = false;
        }
        else
        {
            layer_nr = 0; // because the helper parts are copied from the initial layer in the filler layer
            include_adhesion = false;
        }
    }
    else if (layer_nr > 0 || getSettingAsPlatformAdhesion("adhesion_type") == EPlatformAdhesion::RAFT)
    { // only include adhesion only for layers where platform adhesion actually occurs
        // i.e. layers < 0 are for raft, layer 0 is for brim/skirt
        include_adhesion = false;
    }
    if (include_adhesion)
    {
        ret[getSettingAsIndex("adhesion_extruder_nr")] = true;
        { // process brim/skirt
            for (int extr_nr = 0; extr_nr < meshgroup->getExtruderCount(); extr_nr++)
            {
                if (skirt_brim[extr_nr].size() > 0)
                {
                    ret[extr_nr] = true;
                    continue;
                }
            }
        }
    }

    // TODO: ooze shield, draft shield ..?

    if (include_helper_parts)
    {
        // support
        if (layer_nr < int(support.supportLayers.size()))
        {
            const SupportLayer& support_layer = support.supportLayers[layer_nr];
            if (layer_nr == 0)
            {
                if (!support_layer.supportAreas.empty())
                {
                    ret[getSettingAsIndex("support_extruder_nr_layer_0")] = true;
                }
            }
            else
            {
                if (!support_layer.supportAreas.empty())
                {
                    ret[getSettingAsIndex("support_infill_extruder_nr")] = true;
                }
            }
            if (!support_layer.support_bottom.empty())
            {
                ret[getSettingAsIndex("support_bottom_extruder_nr")] = true;
            }
            if (!support_layer.support_roof.empty())
            {
                ret[getSettingAsIndex("support_roof_extruder_nr")] = true;
            }
        }
    }

    if (include_models)
    {
        for (const SliceMeshStorage& mesh : meshes)
        {
            for (unsigned int extruder_nr = 0; extruder_nr <= ret.size(); extruder_nr++)
            {
                ret[extruder_nr] = ret[extruder_nr] || mesh.getExtruderIsUsed(extruder_nr, layer_nr);
            }
        }
    }
    return ret;
}

} // namespace cura<|endoftext|>