<commit_before>// Copyright (c) 2010 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 "views/controls/label.h"

#include <math.h>
#include <limits>

#include "app/l10n_util.h"
#include "app/resource_bundle.h"
#include "app/text_elider.h"
#include "base/i18n/rtl.h"
#include "base/logging.h"
#include "base/utf_string_conversions.h"
#include "gfx/canvas.h"
#include "gfx/color_utils.h"
#include "gfx/font.h"
#include "gfx/insets.h"
#include "views/background.h"

namespace views {

// static
const char Label::kViewClassName[] = "views/Label";
SkColor Label::kEnabledColor, Label::kDisabledColor;

static const int kFocusBorderPadding = 1;

Label::Label() {
  Init(std::wstring(), GetDefaultFont());
}

Label::Label(const std::wstring& text) {
  Init(text, GetDefaultFont());
}

Label::Label(const std::wstring& text, const gfx::Font& font) {
  Init(text, font);
}

Label::~Label() {
}

gfx::Size Label::GetPreferredSize() {
  // Return a size of (0, 0) if the label is not visible and if the
  // collapse_when_hidden_ flag is set.
  // TODO(munjal): This logic probably belongs to the View class. But for now,
  // put it here since putting it in View class means all inheriting classes
  // need ot respect the collapse_when_hidden_ flag.
  if (!IsVisible() && collapse_when_hidden_)
    return gfx::Size();

  gfx::Size prefsize(GetTextSize());
  gfx::Insets insets = GetInsets();
  prefsize.Enlarge(insets.width(), insets.height());
  return prefsize;
}

int Label::GetBaseline() {
  return GetInsets().top() + font_.baseline();
}

int Label::GetHeightForWidth(int w) {
  return is_multi_line_ ?
      (GetTextSize().height() + GetInsets().height()) :
      View::GetHeightForWidth(w);
}

void Label::DidChangeBounds(const gfx::Rect& previous,
                            const gfx::Rect& current) {
  text_size_valid_ &= !is_multi_line_;
}

void Label::Paint(gfx::Canvas* canvas) {
  PaintBackground(canvas);
  std::wstring paint_text;
  gfx::Rect text_bounds;
  int flags = 0;
  CalculateDrawStringParams(&paint_text, &text_bounds, &flags);
  canvas->DrawStringInt(paint_text, font_, color_,
                        text_bounds.x(), text_bounds.y(),
                        text_bounds.width(), text_bounds.height(), flags);

  if (HasFocus() || paint_as_focused_) {
    text_bounds.Inset(-kFocusBorderPadding, -kFocusBorderPadding);
    // If the label is a single line of text, then the computed text bound
    // corresponds directly to the text being drawn and no mirroring is needed
    // for the RTL case. For multiline text, the text bound is an estimation
    // and is recomputed in gfx::Canvas::SizeStringInt(). For multiline text
    // in RTL, we need to take mirroring into account when computing the focus
    // rectangle.
    if (flags & gfx::Canvas::MULTI_LINE)
      text_bounds.set_x(MirroredLeftPointForRect(text_bounds));
    canvas->DrawFocusRect(text_bounds.x(), text_bounds.y(),
                          text_bounds.width(), text_bounds.height());
  }
}

void Label::PaintBackground(gfx::Canvas* canvas) {
  const Background* bg = contains_mouse_ ? GetMouseOverBackground() : NULL;
  if (!bg)
    bg = background();
  if (bg)
    bg->Paint(canvas, this);
}

void Label::SetFont(const gfx::Font& font) {
  font_ = font;
  text_size_valid_ = false;
  SchedulePaint();
}

void Label::SetText(const std::wstring& text) {
  text_ = text;
  url_set_ = false;
  text_size_valid_ = false;
  SchedulePaint();
}

const std::wstring Label::GetText() const {
  return url_set_ ? UTF8ToWide(url_.spec()) : text_;
}

void Label::SetURL(const GURL& url) {
  url_ = url;
  text_ = UTF8ToWide(url_.spec());
  url_set_ = true;
  text_size_valid_ = false;
  SchedulePaint();
}

const GURL Label::GetURL() const {
  return url_set_ ? url_ : GURL(WideToUTF8(text_));
}

void Label::SetHorizontalAlignment(Alignment a) {
  // If the View's UI layout is right-to-left and rtl_alignment_mode_ is
  // USE_UI_ALIGNMENT, we need to flip the alignment so that the alignment
  // settings take into account the text directionality.
  if (UILayoutIsRightToLeft() && (rtl_alignment_mode_ == USE_UI_ALIGNMENT) &&
      (a != ALIGN_CENTER))
    a = (a == ALIGN_LEFT) ? ALIGN_RIGHT : ALIGN_LEFT;
  if (horiz_alignment_ != a) {
    horiz_alignment_ = a;
    SchedulePaint();
  }
}

void Label::SetMultiLine(bool f) {
  if (f != is_multi_line_) {
    is_multi_line_ = f;
    text_size_valid_ = false;
    SchedulePaint();
  }
}

void Label::SetAllowCharacterBreak(bool f) {
  if (f != allow_character_break_) {
    allow_character_break_ = f;
    text_size_valid_ = false;
    SchedulePaint();
  }
}

void Label::SetTooltipText(const std::wstring& tooltip_text) {
  tooltip_text_ = tooltip_text;
}

bool Label::GetTooltipText(const gfx::Point& p, std::wstring* tooltip) {
  DCHECK(tooltip);

  // If a tooltip has been explicitly set, use it.
  if (!tooltip_text_.empty()) {
    tooltip->assign(tooltip_text_);
    return true;
  }

  // Show the full text if the text does not fit.
  if (!is_multi_line_ && font_.GetStringWidth(text_) > width()) {
    *tooltip = text_;
    return true;
  }
  return false;
}

void Label::OnMouseMoved(const MouseEvent& e) {
  UpdateContainsMouse(e);
}

void Label::OnMouseEntered(const MouseEvent& event) {
  UpdateContainsMouse(event);
}

void Label::OnMouseExited(const MouseEvent& event) {
  SetContainsMouse(false);
}

void Label::SetMouseOverBackground(Background* background) {
  mouse_over_background_.reset(background);
}

const Background* Label::GetMouseOverBackground() const {
  return mouse_over_background_.get();
}

void Label::SetEnabled(bool enabled) {
  if (enabled == enabled_)
    return;
  View::SetEnabled(enabled);
  SetColor(enabled ? kEnabledColor : kDisabledColor);
}

gfx::Insets Label::GetInsets() const {
  gfx::Insets insets = View::GetInsets();
  if (IsFocusable() || has_focus_border_)  {
    insets += gfx::Insets(kFocusBorderPadding, kFocusBorderPadding,
                          kFocusBorderPadding, kFocusBorderPadding);
  }
  return insets;
}

void Label::SizeToFit(int max_width) {
  DCHECK(is_multi_line_);

  std::vector<std::wstring> lines;
  SplitString(text_, L'\n', &lines);

  int label_width = 0;
  for (std::vector<std::wstring>::const_iterator iter = lines.begin();
       iter != lines.end(); ++iter)
    label_width = std::max(label_width, font_.GetStringWidth(*iter));

  label_width += GetInsets().width();

  if (max_width > 0)
    label_width = std::min(label_width, max_width);

  SetBounds(x(), y(), label_width, 0);
  SizeToPreferredSize();
}

bool Label::GetAccessibleRole(AccessibilityTypes::Role* role) {
  DCHECK(role);

  *role = AccessibilityTypes::ROLE_TEXT;
  return true;
}

bool Label::GetAccessibleName(std::wstring* name) {
  DCHECK(name);
  *name = GetText();
  return !name->empty();
}

bool Label::GetAccessibleState(AccessibilityTypes::State* state) {
  DCHECK(state);

  *state = AccessibilityTypes::STATE_READONLY;
  return true;
}

void Label::SetHasFocusBorder(bool has_focus_border) {
  has_focus_border_ = has_focus_border;
  text_size_valid_ &= !is_multi_line_;
}

// static
gfx::Font Label::GetDefaultFont() {
  return ResourceBundle::GetSharedInstance().GetFont(ResourceBundle::BaseFont);
}

void Label::Init(const std::wstring& text, const gfx::Font& font) {
  static bool initialized = false;
  if (!initialized) {
#if defined(OS_WIN)
    kEnabledColor = color_utils::GetSysSkColor(COLOR_WINDOWTEXT);
    kDisabledColor = color_utils::GetSysSkColor(COLOR_GRAYTEXT);
#else
    // TODO(beng): source from theme provider.
    kEnabledColor = SK_ColorBLACK;
    kDisabledColor = SK_ColorGRAY;
#endif

    initialized = true;
  }

  contains_mouse_ = false;
  font_ = font;
  text_size_valid_ = false;
  SetText(text);
  url_set_ = false;
  color_ = kEnabledColor;
  horiz_alignment_ = ALIGN_CENTER;
  is_multi_line_ = false;
  allow_character_break_ = false;
  collapse_when_hidden_ = false;
  rtl_alignment_mode_ = USE_UI_ALIGNMENT;
  paint_as_focused_ = false;
  has_focus_border_ = false;
}

void Label::CalculateDrawStringParams(std::wstring* paint_text,
                                      gfx::Rect* text_bounds,
                                      int* flags) const {
  DCHECK(paint_text && text_bounds && flags);

  if (url_set_) {
    // TODO(jungshik) : Figure out how to get 'intl.accept_languages'
    // preference and use it when calling ElideUrl.
    *paint_text = gfx::ElideUrl(url_, font_, width(), std::wstring());

    // An URLs is always treated as an LTR text and therefore we should
    // explicitly mark it as such if the locale is RTL so that URLs containing
    // Hebrew or Arabic characters are displayed correctly.
    //
    // Note that we don't check the View's UI layout setting in order to
    // determine whether or not to insert the special Unicode formatting
    // characters. We use the locale settings because an URL is always treated
    // as an LTR string, even if its containing view does not use an RTL UI
    // layout.
    if (base::i18n::IsRTL())
      base::i18n::WrapStringWithLTRFormatting(paint_text);
  } else {
    *paint_text = text_;
  }

  *text_bounds = GetTextBounds();
  *flags = ComputeMultiLineFlags();
}

void Label::UpdateContainsMouse(const MouseEvent& event) {
  SetContainsMouse(GetTextBounds().Contains(event.x(), event.y()));
}

void Label::SetContainsMouse(bool contains_mouse) {
  if (contains_mouse_ == contains_mouse)
    return;
  contains_mouse_ = contains_mouse;
  if (GetMouseOverBackground())
    SchedulePaint();
}

gfx::Rect Label::GetTextBounds() const {
  gfx::Rect available_rect(GetAvailableRect());
  gfx::Size text_size(GetTextSize());
  text_size.set_width(std::min(available_rect.width(), text_size.width()));

  gfx::Insets insets = GetInsets();
  gfx::Point text_origin(insets.left(), insets.top());
  switch (horiz_alignment_) {
    case ALIGN_LEFT:
      break;
    case ALIGN_CENTER:
      // We put any extra margin pixel on the left rather than the right, since
      // GetTextExtentPoint32() can report a value one too large on the right.
      text_origin.Offset((available_rect.width() + 1 - text_size.width()) / 2,
                         0);
      break;
    case ALIGN_RIGHT:
      text_origin.set_x(available_rect.right() - text_size.width());
      break;
    default:
      NOTREACHED();
      break;
  }
  text_origin.Offset(0,
      std::max(0, (available_rect.height() - text_size.height())) / 2);
  return gfx::Rect(text_origin, text_size);
}

gfx::Size Label::GetTextSize() const {
  if (!text_size_valid_) {
    int w = is_multi_line_ ?
        GetAvailableRect().width() : std::numeric_limits<int>::max();
    int h = font_.height();
    gfx::Canvas::SizeStringInt(text_, font_, &w, &h, ComputeMultiLineFlags());
    text_size_.SetSize(w, h);
    text_size_valid_ = true;
  }

  return text_size_;
}

int Label::ComputeMultiLineFlags() const {
  if (!is_multi_line_)
    return 0;

  int flags = gfx::Canvas::MULTI_LINE;
#if !defined(OS_WIN)
    // Don't ellide multiline labels on Linux.
    // Todo(davemoore): Do we depend on elliding multiline text?
    // Pango insists on limiting the number of lines to one if text is
    // ellided. You can get around this if you can pass a maximum height
    // but we don't currently have that data when we call the pango code.
    flags |= gfx::Canvas::NO_ELLIPSIS;
#endif
  if (allow_character_break_)
    flags |= gfx::Canvas::CHARACTER_BREAK;
  switch (horiz_alignment_) {
    case ALIGN_LEFT:
      flags |= gfx::Canvas::TEXT_ALIGN_LEFT;
      break;
    case ALIGN_CENTER:
      flags |= gfx::Canvas::TEXT_ALIGN_CENTER;
      break;
    case ALIGN_RIGHT:
      flags |= gfx::Canvas::TEXT_ALIGN_RIGHT;
      break;
  }
  return flags;
}

gfx::Rect Label::GetAvailableRect() const {
  gfx::Rect bounds(gfx::Point(), size());
  gfx::Insets insets(GetInsets());
  bounds.Inset(insets.left(), insets.top(), insets.right(), insets.bottom());
  return bounds;
}

}  // namespace views
<commit_msg>Fix regression from Label fixes -- I forgot to make GetHeightForWidth() actually care about the provided width for multi-line text.  Oops.<commit_after>// Copyright (c) 2010 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 "views/controls/label.h"

#include <cmath>

#include "app/l10n_util.h"
#include "app/resource_bundle.h"
#include "app/text_elider.h"
#include "base/i18n/rtl.h"
#include "base/logging.h"
#include "base/utf_string_conversions.h"
#include "gfx/canvas.h"
#include "gfx/color_utils.h"
#include "gfx/font.h"
#include "gfx/insets.h"
#include "views/background.h"

namespace views {

// static
const char Label::kViewClassName[] = "views/Label";
SkColor Label::kEnabledColor, Label::kDisabledColor;

static const int kFocusBorderPadding = 1;

Label::Label() {
  Init(std::wstring(), GetDefaultFont());
}

Label::Label(const std::wstring& text) {
  Init(text, GetDefaultFont());
}

Label::Label(const std::wstring& text, const gfx::Font& font) {
  Init(text, font);
}

Label::~Label() {
}

gfx::Size Label::GetPreferredSize() {
  // Return a size of (0, 0) if the label is not visible and if the
  // collapse_when_hidden_ flag is set.
  // TODO(munjal): This logic probably belongs to the View class. But for now,
  // put it here since putting it in View class means all inheriting classes
  // need ot respect the collapse_when_hidden_ flag.
  if (!IsVisible() && collapse_when_hidden_)
    return gfx::Size();

  gfx::Size prefsize(GetTextSize());
  gfx::Insets insets = GetInsets();
  prefsize.Enlarge(insets.width(), insets.height());
  return prefsize;
}

int Label::GetBaseline() {
  return GetInsets().top() + font_.baseline();
}

int Label::GetHeightForWidth(int w) {
  if (!is_multi_line_)
    return View::GetHeightForWidth(w);

  w = std::max(0, w - GetInsets().width());
  int h = font_.height();
  gfx::Canvas::SizeStringInt(text_, font_, &w, &h, ComputeMultiLineFlags());
  return h + GetInsets().height();
}

void Label::DidChangeBounds(const gfx::Rect& previous,
                            const gfx::Rect& current) {
  text_size_valid_ &= !is_multi_line_;
}

void Label::Paint(gfx::Canvas* canvas) {
  PaintBackground(canvas);
  std::wstring paint_text;
  gfx::Rect text_bounds;
  int flags = 0;
  CalculateDrawStringParams(&paint_text, &text_bounds, &flags);
  canvas->DrawStringInt(paint_text, font_, color_,
                        text_bounds.x(), text_bounds.y(),
                        text_bounds.width(), text_bounds.height(), flags);

  if (HasFocus() || paint_as_focused_) {
    text_bounds.Inset(-kFocusBorderPadding, -kFocusBorderPadding);
    // If the label is a single line of text, then the computed text bound
    // corresponds directly to the text being drawn and no mirroring is needed
    // for the RTL case. For multiline text, the text bound is an estimation
    // and is recomputed in gfx::Canvas::SizeStringInt(). For multiline text
    // in RTL, we need to take mirroring into account when computing the focus
    // rectangle.
    if (flags & gfx::Canvas::MULTI_LINE)
      text_bounds.set_x(MirroredLeftPointForRect(text_bounds));
    canvas->DrawFocusRect(text_bounds.x(), text_bounds.y(),
                          text_bounds.width(), text_bounds.height());
  }
}

void Label::PaintBackground(gfx::Canvas* canvas) {
  const Background* bg = contains_mouse_ ? GetMouseOverBackground() : NULL;
  if (!bg)
    bg = background();
  if (bg)
    bg->Paint(canvas, this);
}

void Label::SetFont(const gfx::Font& font) {
  font_ = font;
  text_size_valid_ = false;
  SchedulePaint();
}

void Label::SetText(const std::wstring& text) {
  text_ = text;
  url_set_ = false;
  text_size_valid_ = false;
  SchedulePaint();
}

const std::wstring Label::GetText() const {
  return url_set_ ? UTF8ToWide(url_.spec()) : text_;
}

void Label::SetURL(const GURL& url) {
  url_ = url;
  text_ = UTF8ToWide(url_.spec());
  url_set_ = true;
  text_size_valid_ = false;
  SchedulePaint();
}

const GURL Label::GetURL() const {
  return url_set_ ? url_ : GURL(WideToUTF8(text_));
}

void Label::SetHorizontalAlignment(Alignment a) {
  // If the View's UI layout is right-to-left and rtl_alignment_mode_ is
  // USE_UI_ALIGNMENT, we need to flip the alignment so that the alignment
  // settings take into account the text directionality.
  if (UILayoutIsRightToLeft() && (rtl_alignment_mode_ == USE_UI_ALIGNMENT) &&
      (a != ALIGN_CENTER))
    a = (a == ALIGN_LEFT) ? ALIGN_RIGHT : ALIGN_LEFT;
  if (horiz_alignment_ != a) {
    horiz_alignment_ = a;
    SchedulePaint();
  }
}

void Label::SetMultiLine(bool f) {
  if (f != is_multi_line_) {
    is_multi_line_ = f;
    text_size_valid_ = false;
    SchedulePaint();
  }
}

void Label::SetAllowCharacterBreak(bool f) {
  if (f != allow_character_break_) {
    allow_character_break_ = f;
    text_size_valid_ = false;
    SchedulePaint();
  }
}

void Label::SetTooltipText(const std::wstring& tooltip_text) {
  tooltip_text_ = tooltip_text;
}

bool Label::GetTooltipText(const gfx::Point& p, std::wstring* tooltip) {
  DCHECK(tooltip);

  // If a tooltip has been explicitly set, use it.
  if (!tooltip_text_.empty()) {
    tooltip->assign(tooltip_text_);
    return true;
  }

  // Show the full text if the text does not fit.
  if (!is_multi_line_ && font_.GetStringWidth(text_) > width()) {
    *tooltip = text_;
    return true;
  }
  return false;
}

void Label::OnMouseMoved(const MouseEvent& e) {
  UpdateContainsMouse(e);
}

void Label::OnMouseEntered(const MouseEvent& event) {
  UpdateContainsMouse(event);
}

void Label::OnMouseExited(const MouseEvent& event) {
  SetContainsMouse(false);
}

void Label::SetMouseOverBackground(Background* background) {
  mouse_over_background_.reset(background);
}

const Background* Label::GetMouseOverBackground() const {
  return mouse_over_background_.get();
}

void Label::SetEnabled(bool enabled) {
  if (enabled == enabled_)
    return;
  View::SetEnabled(enabled);
  SetColor(enabled ? kEnabledColor : kDisabledColor);
}

gfx::Insets Label::GetInsets() const {
  gfx::Insets insets = View::GetInsets();
  if (IsFocusable() || has_focus_border_)  {
    insets += gfx::Insets(kFocusBorderPadding, kFocusBorderPadding,
                          kFocusBorderPadding, kFocusBorderPadding);
  }
  return insets;
}

void Label::SizeToFit(int max_width) {
  DCHECK(is_multi_line_);

  std::vector<std::wstring> lines;
  SplitString(text_, L'\n', &lines);

  int label_width = 0;
  for (std::vector<std::wstring>::const_iterator iter = lines.begin();
       iter != lines.end(); ++iter)
    label_width = std::max(label_width, font_.GetStringWidth(*iter));

  label_width += GetInsets().width();

  if (max_width > 0)
    label_width = std::min(label_width, max_width);

  SetBounds(x(), y(), label_width, 0);
  SizeToPreferredSize();
}

bool Label::GetAccessibleRole(AccessibilityTypes::Role* role) {
  DCHECK(role);

  *role = AccessibilityTypes::ROLE_TEXT;
  return true;
}

bool Label::GetAccessibleName(std::wstring* name) {
  DCHECK(name);
  *name = GetText();
  return !name->empty();
}

bool Label::GetAccessibleState(AccessibilityTypes::State* state) {
  DCHECK(state);

  *state = AccessibilityTypes::STATE_READONLY;
  return true;
}

void Label::SetHasFocusBorder(bool has_focus_border) {
  has_focus_border_ = has_focus_border;
  text_size_valid_ &= !is_multi_line_;
}

// static
gfx::Font Label::GetDefaultFont() {
  return ResourceBundle::GetSharedInstance().GetFont(ResourceBundle::BaseFont);
}

void Label::Init(const std::wstring& text, const gfx::Font& font) {
  static bool initialized = false;
  if (!initialized) {
#if defined(OS_WIN)
    kEnabledColor = color_utils::GetSysSkColor(COLOR_WINDOWTEXT);
    kDisabledColor = color_utils::GetSysSkColor(COLOR_GRAYTEXT);
#else
    // TODO(beng): source from theme provider.
    kEnabledColor = SK_ColorBLACK;
    kDisabledColor = SK_ColorGRAY;
#endif

    initialized = true;
  }

  contains_mouse_ = false;
  font_ = font;
  text_size_valid_ = false;
  SetText(text);
  url_set_ = false;
  color_ = kEnabledColor;
  horiz_alignment_ = ALIGN_CENTER;
  is_multi_line_ = false;
  allow_character_break_ = false;
  collapse_when_hidden_ = false;
  rtl_alignment_mode_ = USE_UI_ALIGNMENT;
  paint_as_focused_ = false;
  has_focus_border_ = false;
}

void Label::CalculateDrawStringParams(std::wstring* paint_text,
                                      gfx::Rect* text_bounds,
                                      int* flags) const {
  DCHECK(paint_text && text_bounds && flags);

  if (url_set_) {
    // TODO(jungshik) : Figure out how to get 'intl.accept_languages'
    // preference and use it when calling ElideUrl.
    *paint_text = gfx::ElideUrl(url_, font_, width(), std::wstring());

    // An URLs is always treated as an LTR text and therefore we should
    // explicitly mark it as such if the locale is RTL so that URLs containing
    // Hebrew or Arabic characters are displayed correctly.
    //
    // Note that we don't check the View's UI layout setting in order to
    // determine whether or not to insert the special Unicode formatting
    // characters. We use the locale settings because an URL is always treated
    // as an LTR string, even if its containing view does not use an RTL UI
    // layout.
    if (base::i18n::IsRTL())
      base::i18n::WrapStringWithLTRFormatting(paint_text);
  } else {
    *paint_text = text_;
  }

  *text_bounds = GetTextBounds();
  *flags = ComputeMultiLineFlags();
}

void Label::UpdateContainsMouse(const MouseEvent& event) {
  SetContainsMouse(GetTextBounds().Contains(event.x(), event.y()));
}

void Label::SetContainsMouse(bool contains_mouse) {
  if (contains_mouse_ == contains_mouse)
    return;
  contains_mouse_ = contains_mouse;
  if (GetMouseOverBackground())
    SchedulePaint();
}

gfx::Rect Label::GetTextBounds() const {
  gfx::Rect available_rect(GetAvailableRect());
  gfx::Size text_size(GetTextSize());
  text_size.set_width(std::min(available_rect.width(), text_size.width()));

  gfx::Insets insets = GetInsets();
  gfx::Point text_origin(insets.left(), insets.top());
  switch (horiz_alignment_) {
    case ALIGN_LEFT:
      break;
    case ALIGN_CENTER:
      // We put any extra margin pixel on the left rather than the right, since
      // GetTextExtentPoint32() can report a value one too large on the right.
      text_origin.Offset((available_rect.width() + 1 - text_size.width()) / 2,
                         0);
      break;
    case ALIGN_RIGHT:
      text_origin.set_x(available_rect.right() - text_size.width());
      break;
    default:
      NOTREACHED();
      break;
  }
  text_origin.Offset(0,
      std::max(0, (available_rect.height() - text_size.height())) / 2);
  return gfx::Rect(text_origin, text_size);
}

gfx::Size Label::GetTextSize() const {
  if (!text_size_valid_) {
    int w = GetAvailableRect().width();
    int h = font_.height();
    // For single-line strings, ignore the available width and calculate how
    // wide the text wants to be.
    int flags = ComputeMultiLineFlags();
    if (!is_multi_line_)
      flags |= gfx::Canvas::NO_ELLIPSIS;
    gfx::Canvas::SizeStringInt(text_, font_, &w, &h, flags);
    text_size_.SetSize(w, h);
    text_size_valid_ = true;
  }

  return text_size_;
}

int Label::ComputeMultiLineFlags() const {
  if (!is_multi_line_)
    return 0;

  int flags = gfx::Canvas::MULTI_LINE;
#if !defined(OS_WIN)
    // Don't elide multiline labels on Linux.
    // Todo(davemoore): Do we depend on eliding multiline text?
    // Pango insists on limiting the number of lines to one if text is
    // elided. You can get around this if you can pass a maximum height
    // but we don't currently have that data when we call the pango code.
    flags |= gfx::Canvas::NO_ELLIPSIS;
#endif
  if (allow_character_break_)
    flags |= gfx::Canvas::CHARACTER_BREAK;
  switch (horiz_alignment_) {
    case ALIGN_LEFT:
      flags |= gfx::Canvas::TEXT_ALIGN_LEFT;
      break;
    case ALIGN_CENTER:
      flags |= gfx::Canvas::TEXT_ALIGN_CENTER;
      break;
    case ALIGN_RIGHT:
      flags |= gfx::Canvas::TEXT_ALIGN_RIGHT;
      break;
  }
  return flags;
}

gfx::Rect Label::GetAvailableRect() const {
  gfx::Rect bounds(gfx::Point(), size());
  gfx::Insets insets(GetInsets());
  bounds.Inset(insets.left(), insets.top(), insets.right(), insets.bottom());
  return bounds;
}

}  // namespace views
<|endoftext|>


<commit_before>/*
-----------------------------------------------------------------------------
This source file is part of OGRE
    (Object-oriented Graphics Rendering Engine)
For the latest info, see http://www.ogre3d.org

Copyright (c) 2008 Renato Araujo Oliveira Filho <renatox@gmail.com>
Copyright (c) 2000-2009 Torus Knot Software Ltd

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-----------------------------------------------------------------------------
*/

#include "OgreGLESPixelFormat.h"

#include "OgreRoot.h"
#include "OgreRenderSystem.h"
#include "OgreBitwise.h"

namespace Ogre  {
    GLenum GLESPixelUtil::getGLOriginFormat(PixelFormat mFormat)
    {
        switch (mFormat)
        {
            case PF_A8:
                return GL_ALPHA;

            case PF_L8:
            case PF_L16:
            case PF_FLOAT16_R:
            case PF_FLOAT32_R:
                return GL_LUMINANCE;

            case PF_BYTE_LA:
            case PF_SHORT_GR:
            case PF_FLOAT16_GR:
            case PF_FLOAT32_GR:
                return GL_LUMINANCE_ALPHA;

            // PVR compressed formats
            case PF_PVR_RGB2:
                return GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG;
            case PF_PVR_RGB4:
                return GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG;
            case PF_PVR_RGBA2:
                return GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG;
            case PF_PVR_RGBA4:
                return GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG;

            case PF_R3G3B2:
            case PF_R5G6B5:
            case PF_FLOAT16_RGB:
            case PF_FLOAT32_RGB:
            case PF_SHORT_RGB:
                return GL_RGB;

            case PF_X8R8G8B8:
            case PF_A8R8G8B8:
            case PF_B8G8R8A8:
            case PF_A1R5G5B5:
            case PF_A4R4G4B4:
            case PF_A2R10G10B10:
// This case in incorrect, swaps R & B channels
//                return GL_BGRA;

            case PF_X8B8G8R8:
            case PF_A8B8G8R8:
            case PF_A2B10G10R10:
            case PF_FLOAT16_RGBA:
            case PF_FLOAT32_RGBA:
            case PF_SHORT_RGBA:
                return GL_RGBA;

#if OGRE_ENDIAN == OGRE_ENDIAN_BIG
            // Formats are in native endian, so R8G8B8 on little endian is
            // BGR, on big endian it is RGB.
            case PF_R8G8B8:
                return GL_RGB;
            case PF_B8G8R8:
                return 0;
#else
            case PF_R8G8B8:
                return 0;
            case PF_B8G8R8:
                return GL_RGB;
#endif
            case PF_DXT1:
            case PF_DXT3:
            case PF_DXT5:
            case PF_B5G6R5:
            default:
                return 0;
        }
    }

    GLenum GLESPixelUtil::getGLOriginDataType(PixelFormat mFormat)
    {
        switch (mFormat)
        {
            case PF_A8:
            case PF_L8:
            case PF_R8G8B8:
            case PF_B8G8R8:
            case PF_BYTE_LA:
            case PF_PVR_RGB2:
            case PF_PVR_RGB4:
            case PF_PVR_RGBA2:
            case PF_PVR_RGBA4:
                return GL_UNSIGNED_BYTE;
            case PF_R5G6B5:
            case PF_B5G6R5:
                return GL_UNSIGNED_SHORT_5_6_5;
            case PF_L16:
                return GL_UNSIGNED_SHORT;

#if OGRE_ENDIAN == OGRE_ENDIAN_BIG
            case PF_X8B8G8R8:
            case PF_A8B8G8R8:
                return GL_UNSIGNED_INT_8_8_8_8_REV;
            case PF_X8R8G8B8:
            case PF_A8R8G8B8:
                return GL_UNSIGNED_INT_8_8_8_8_REV;
            case PF_B8G8R8A8:
                return GL_UNSIGNED_BYTE;
            case PF_R8G8B8A8:
                return GL_UNSIGNED_BYTE;
#else
            case PF_X8B8G8R8:
            case PF_A8B8G8R8:
            case PF_X8R8G8B8:
            case PF_A8R8G8B8:
                return GL_UNSIGNED_BYTE;
            case PF_B8G8R8A8:
            case PF_R8G8B8A8:
                return 0;
#endif

            case PF_FLOAT32_R:
            case PF_FLOAT32_GR:
            case PF_FLOAT32_RGB:
            case PF_FLOAT32_RGBA:
                return GL_FLOAT;
            case PF_SHORT_RGBA:
            case PF_SHORT_RGB:
            case PF_SHORT_GR:
                return GL_UNSIGNED_SHORT;

            case PF_A2R10G10B10:
            case PF_A2B10G10R10:
            case PF_FLOAT16_R:
            case PF_FLOAT16_GR:
            case PF_FLOAT16_RGB:
            case PF_FLOAT16_RGBA:
            case PF_R3G3B2:
            case PF_A1R5G5B5:
            case PF_A4R4G4B4:
                // TODO not supported
            default:
                return 0;
        }
    }

    GLenum GLESPixelUtil::getGLInternalFormat(PixelFormat fmt, bool hwGamma)
    {
        switch (fmt)
        {
            case PF_L8:
                return GL_LUMINANCE;

            case PF_A8:
                return GL_ALPHA;

            case PF_BYTE_LA:
                return GL_LUMINANCE_ALPHA;

            case PF_PVR_RGB2:
                return GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG;
            case PF_PVR_RGB4:
                return GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG;
            case PF_PVR_RGBA2:
                return GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG;
            case PF_PVR_RGBA4:
                return GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG;

            case PF_R8G8B8:
            case PF_B8G8R8:
            case PF_X8B8G8R8:
            case PF_X8R8G8B8:
                // DJR - Commenting this out resolves texture problems on iPhone
//                if (!hwGamma)
//                {
//                    return GL_RGB;
//                }
            case PF_A8R8G8B8:
            case PF_B8G8R8A8:
                if (!hwGamma)
                {
                    return GL_RGBA;
                }
            case PF_A4L4:
            case PF_L16:
            case PF_A4R4G4B4:
            case PF_R3G3B2:
            case PF_A1R5G5B5:
            case PF_R5G6B5:
            case PF_B5G6R5:
            case PF_A2R10G10B10:
            case PF_A2B10G10R10:
            case PF_FLOAT16_R:
            case PF_FLOAT16_RGB:
            case PF_FLOAT16_GR:
            case PF_FLOAT16_RGBA:
            case PF_FLOAT32_R:
            case PF_FLOAT32_GR:
            case PF_FLOAT32_RGB:
            case PF_FLOAT32_RGBA:
            case PF_SHORT_RGBA:
            case PF_SHORT_RGB:
            case PF_SHORT_GR:
            case PF_DXT1:
            case PF_DXT3:
            case PF_DXT5:
            default:
                return 0;
        }
    }

    GLenum GLESPixelUtil::getClosestGLInternalFormat(PixelFormat mFormat,
                                                   bool hwGamma)
    {
        GLenum format = getGLInternalFormat(mFormat, hwGamma);
        if (format==0)
        {
            if (hwGamma)
            {
                // TODO not supported
                return 0;
            }
            else
            {
                return GL_RGBA;
            }
        }
        else
        {
            return format;
        }
    }

    PixelFormat GLESPixelUtil::getClosestOGREFormat(GLenum fmt)
    {
        switch (fmt)
        {
            case GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG:
                return PF_PVR_RGB2;
            case GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG:
                return PF_PVR_RGBA2;
            case GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG:
                return PF_PVR_RGB4;
            case GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG:
                return PF_PVR_RGBA4;
            case GL_LUMINANCE:
                return PF_L8;
            case GL_ALPHA:
                return PF_A8;
            case GL_LUMINANCE_ALPHA:
                return PF_BYTE_LA;
            case GL_RGB:
                return PF_X8R8G8B8;
            case GL_RGBA:
                return PF_A8R8G8B8;
#ifdef GL_BGRA
            case GL_BGRA:
#endif
//                return PF_X8B8G8R8;
            default:
                //TODO: not supported
                return PF_A8R8G8B8;
        };
    }

    size_t GLESPixelUtil::getMaxMipmaps(size_t width, size_t height, size_t depth,
                                      PixelFormat format)
    {
        size_t count = 0;

        do {
            if (width > 1)
            {
                width = width / 2;
            }
            if (height > 1)
            {
                height = height / 2;
            }
            if (depth > 1)
            {
                depth = depth / 2;
            }
            /*
            NOT needed, compressed formats will have mipmaps up to 1x1
            if(PixelUtil::isValidExtent(width, height, depth, format))
                count ++;
            else
                break;
            */
            count++;
        } while (!(width == 1 && height == 1 && depth == 1));

        return count;
    }

    size_t GLESPixelUtil::optionalPO2(size_t value)
    {
        const RenderSystemCapabilities *caps =
            Root::getSingleton().getRenderSystem()->getCapabilities();

        if (caps->hasCapability(RSC_NON_POWER_OF_2_TEXTURES))
        {
            return value;
        }
        else
        {
            return Bitwise::firstPO2From((uint32)value);
        }
    }

    PixelBox* GLESPixelUtil::convertToGLformat(const PixelBox &data,
                                             GLenum *outputFormat)
    {
        GLenum glFormat = GLESPixelUtil::getGLOriginFormat(data.format);
        if (glFormat != 0)
        {
            // format already supported
            return OGRE_NEW PixelBox(data);
        }

        PixelBox *converted = 0;

        if (data.format == PF_R8G8B8)
        {
            // Convert BGR -> RGB
            converted->format = PF_B8G8R8;
            *outputFormat = GL_RGB;

            converted = OGRE_NEW PixelBox(data);
            uint32 *data = (uint32 *) converted->data;
            for (uint i = 0; i < converted->getWidth() * converted->getHeight(); i++)
            {
                uint32 *color = data;
                *color = (*color & 0x000000ff) << 16 |
                         (*color & 0x0000FF00) |
                         (*color & 0x00FF0000) >> 16;
                data += 1;
            }
        }

        return converted;
    }
}
<commit_msg>Didn't mean to check this in, PVR texture support isn't finished yet.  Commenting it out for now<commit_after>/*
-----------------------------------------------------------------------------
This source file is part of OGRE
    (Object-oriented Graphics Rendering Engine)
For the latest info, see http://www.ogre3d.org

Copyright (c) 2008 Renato Araujo Oliveira Filho <renatox@gmail.com>
Copyright (c) 2000-2009 Torus Knot Software Ltd

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-----------------------------------------------------------------------------
*/

#include "OgreGLESPixelFormat.h"

#include "OgreRoot.h"
#include "OgreRenderSystem.h"
#include "OgreBitwise.h"

namespace Ogre  {
    GLenum GLESPixelUtil::getGLOriginFormat(PixelFormat mFormat)
    {
        switch (mFormat)
        {
            case PF_A8:
                return GL_ALPHA;

            case PF_L8:
            case PF_L16:
            case PF_FLOAT16_R:
            case PF_FLOAT32_R:
                return GL_LUMINANCE;

            case PF_BYTE_LA:
            case PF_SHORT_GR:
            case PF_FLOAT16_GR:
            case PF_FLOAT32_GR:
                return GL_LUMINANCE_ALPHA;

            // PVR compressed formats
//            case PF_PVR_RGB2:
//                return GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG;
//            case PF_PVR_RGB4:
//                return GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG;
//            case PF_PVR_RGBA2:
//                return GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG;
//            case PF_PVR_RGBA4:
//                return GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG;

            case PF_R3G3B2:
            case PF_R5G6B5:
            case PF_FLOAT16_RGB:
            case PF_FLOAT32_RGB:
            case PF_SHORT_RGB:
                return GL_RGB;

            case PF_X8R8G8B8:
            case PF_A8R8G8B8:
            case PF_B8G8R8A8:
            case PF_A1R5G5B5:
            case PF_A4R4G4B4:
            case PF_A2R10G10B10:
// This case in incorrect, swaps R & B channels
//                return GL_BGRA;

            case PF_X8B8G8R8:
            case PF_A8B8G8R8:
            case PF_A2B10G10R10:
            case PF_FLOAT16_RGBA:
            case PF_FLOAT32_RGBA:
            case PF_SHORT_RGBA:
                return GL_RGBA;

#if OGRE_ENDIAN == OGRE_ENDIAN_BIG
            // Formats are in native endian, so R8G8B8 on little endian is
            // BGR, on big endian it is RGB.
            case PF_R8G8B8:
                return GL_RGB;
            case PF_B8G8R8:
                return 0;
#else
            case PF_R8G8B8:
                return 0;
            case PF_B8G8R8:
                return GL_RGB;
#endif
            case PF_DXT1:
            case PF_DXT3:
            case PF_DXT5:
            case PF_B5G6R5:
            default:
                return 0;
        }
    }

    GLenum GLESPixelUtil::getGLOriginDataType(PixelFormat mFormat)
    {
        switch (mFormat)
        {
            case PF_A8:
            case PF_L8:
            case PF_R8G8B8:
            case PF_B8G8R8:
            case PF_BYTE_LA:
//            case PF_PVR_RGB2:
//            case PF_PVR_RGB4:
//            case PF_PVR_RGBA2:
//            case PF_PVR_RGBA4:
                return GL_UNSIGNED_BYTE;
            case PF_R5G6B5:
            case PF_B5G6R5:
                return GL_UNSIGNED_SHORT_5_6_5;
            case PF_L16:
                return GL_UNSIGNED_SHORT;

#if OGRE_ENDIAN == OGRE_ENDIAN_BIG
            case PF_X8B8G8R8:
            case PF_A8B8G8R8:
                return GL_UNSIGNED_INT_8_8_8_8_REV;
            case PF_X8R8G8B8:
            case PF_A8R8G8B8:
                return GL_UNSIGNED_INT_8_8_8_8_REV;
            case PF_B8G8R8A8:
                return GL_UNSIGNED_BYTE;
            case PF_R8G8B8A8:
                return GL_UNSIGNED_BYTE;
#else
            case PF_X8B8G8R8:
            case PF_A8B8G8R8:
            case PF_X8R8G8B8:
            case PF_A8R8G8B8:
                return GL_UNSIGNED_BYTE;
            case PF_B8G8R8A8:
            case PF_R8G8B8A8:
                return 0;
#endif

            case PF_FLOAT32_R:
            case PF_FLOAT32_GR:
            case PF_FLOAT32_RGB:
            case PF_FLOAT32_RGBA:
                return GL_FLOAT;
            case PF_SHORT_RGBA:
            case PF_SHORT_RGB:
            case PF_SHORT_GR:
                return GL_UNSIGNED_SHORT;

            case PF_A2R10G10B10:
            case PF_A2B10G10R10:
            case PF_FLOAT16_R:
            case PF_FLOAT16_GR:
            case PF_FLOAT16_RGB:
            case PF_FLOAT16_RGBA:
            case PF_R3G3B2:
            case PF_A1R5G5B5:
            case PF_A4R4G4B4:
                // TODO not supported
            default:
                return 0;
        }
    }

    GLenum GLESPixelUtil::getGLInternalFormat(PixelFormat fmt, bool hwGamma)
    {
        switch (fmt)
        {
            case PF_L8:
                return GL_LUMINANCE;

            case PF_A8:
                return GL_ALPHA;

            case PF_BYTE_LA:
                return GL_LUMINANCE_ALPHA;

//            case PF_PVR_RGB2:
//                return GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG;
//            case PF_PVR_RGB4:
//                return GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG;
//            case PF_PVR_RGBA2:
//                return GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG;
//            case PF_PVR_RGBA4:
//                return GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG;

            case PF_R8G8B8:
            case PF_B8G8R8:
            case PF_X8B8G8R8:
            case PF_X8R8G8B8:
                // DJR - Commenting this out resolves texture problems on iPhone
//                if (!hwGamma)
//                {
//                    return GL_RGB;
//                }
            case PF_A8R8G8B8:
            case PF_B8G8R8A8:
                if (!hwGamma)
                {
                    return GL_RGBA;
                }
            case PF_A4L4:
            case PF_L16:
            case PF_A4R4G4B4:
            case PF_R3G3B2:
            case PF_A1R5G5B5:
            case PF_R5G6B5:
            case PF_B5G6R5:
            case PF_A2R10G10B10:
            case PF_A2B10G10R10:
            case PF_FLOAT16_R:
            case PF_FLOAT16_RGB:
            case PF_FLOAT16_GR:
            case PF_FLOAT16_RGBA:
            case PF_FLOAT32_R:
            case PF_FLOAT32_GR:
            case PF_FLOAT32_RGB:
            case PF_FLOAT32_RGBA:
            case PF_SHORT_RGBA:
            case PF_SHORT_RGB:
            case PF_SHORT_GR:
            case PF_DXT1:
            case PF_DXT3:
            case PF_DXT5:
            default:
                return 0;
        }
    }

    GLenum GLESPixelUtil::getClosestGLInternalFormat(PixelFormat mFormat,
                                                   bool hwGamma)
    {
        GLenum format = getGLInternalFormat(mFormat, hwGamma);
        if (format==0)
        {
            if (hwGamma)
            {
                // TODO not supported
                return 0;
            }
            else
            {
                return GL_RGBA;
            }
        }
        else
        {
            return format;
        }
    }

    PixelFormat GLESPixelUtil::getClosestOGREFormat(GLenum fmt)
    {
        switch (fmt)
        {
//            case GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG:
//                return PF_PVR_RGB2;
//            case GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG:
//                return PF_PVR_RGBA2;
//            case GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG:
//                return PF_PVR_RGB4;
//            case GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG:
//                return PF_PVR_RGBA4;
            case GL_LUMINANCE:
                return PF_L8;
            case GL_ALPHA:
                return PF_A8;
            case GL_LUMINANCE_ALPHA:
                return PF_BYTE_LA;
            case GL_RGB:
                return PF_X8R8G8B8;
            case GL_RGBA:
                return PF_A8R8G8B8;
#ifdef GL_BGRA
            case GL_BGRA:
#endif
//                return PF_X8B8G8R8;
            default:
                //TODO: not supported
                return PF_A8R8G8B8;
        };
    }

    size_t GLESPixelUtil::getMaxMipmaps(size_t width, size_t height, size_t depth,
                                      PixelFormat format)
    {
        size_t count = 0;

        do {
            if (width > 1)
            {
                width = width / 2;
            }
            if (height > 1)
            {
                height = height / 2;
            }
            if (depth > 1)
            {
                depth = depth / 2;
            }
            /*
            NOT needed, compressed formats will have mipmaps up to 1x1
            if(PixelUtil::isValidExtent(width, height, depth, format))
                count ++;
            else
                break;
            */
            count++;
        } while (!(width == 1 && height == 1 && depth == 1));

        return count;
    }

    size_t GLESPixelUtil::optionalPO2(size_t value)
    {
        const RenderSystemCapabilities *caps =
            Root::getSingleton().getRenderSystem()->getCapabilities();

        if (caps->hasCapability(RSC_NON_POWER_OF_2_TEXTURES))
        {
            return value;
        }
        else
        {
            return Bitwise::firstPO2From((uint32)value);
        }
    }

    PixelBox* GLESPixelUtil::convertToGLformat(const PixelBox &data,
                                             GLenum *outputFormat)
    {
        GLenum glFormat = GLESPixelUtil::getGLOriginFormat(data.format);
        if (glFormat != 0)
        {
            // format already supported
            return OGRE_NEW PixelBox(data);
        }

        PixelBox *converted = 0;

        if (data.format == PF_R8G8B8)
        {
            // Convert BGR -> RGB
            converted->format = PF_B8G8R8;
            *outputFormat = GL_RGB;

            converted = OGRE_NEW PixelBox(data);
            uint32 *data = (uint32 *) converted->data;
            for (uint i = 0; i < converted->getWidth() * converted->getHeight(); i++)
            {
                uint32 *color = data;
                *color = (*color & 0x000000ff) << 16 |
                         (*color & 0x0000FF00) |
                         (*color & 0x00FF0000) >> 16;
                data += 1;
            }
        }

        return converted;
    }
}
<|endoftext|>


<commit_before>//Author: John Xu. 6/23/2019
//Initial code drafted on 6/21/2019 on a flight back from Plantation, FL
//
//The initial problem was an Amazon phone screen problem: print out the top K number from a given array of integers
//
//Initally the answer should be simple: take each integer from the array and insert into a priority queue and then
//do a loop to pop from this prioroty queue to print out each of the integer
//
//Then later on, it is asked, what if the array is so big, such as comming from a stream, and does not fit into the memory ?
//My immediate response was: once we reached size K for the priority queue, we remove the last element before insert the
//new element into the priority queue, if the new element is larger than the last element of the priority queue.
//
// Above immediate response has following defects:
//
// 1) priority queue does not have a method to access or remove the last element
//    regular queue has push, pop, front, back, size, empty.
//    dequeue has push_back, push_front, pop_back, pop_front, size, empty
//    priority_queue has push, pop, top, size, empty
//
// 2) over use of priority queue may be too slow. Priority queue better used for cases where the data is really used as
//    a queue structure, where processed elements are gone from the storage, where no need to keep the historical top K
//    element.
//
// 3) If we want to keep the historical top K element from a unlimited stream inout, it is more efficient to maintain a
//    dynamic array of size K. Maintain the array sorted from large to small. When new element comes in, if it is smaller
//    than the last one, ignore it. Otherwise, remove the last one, and shift every element one position to next one if that
//    element is smaller than the new one, then insert the new one at the end of the shift.
//

//example of keep top 3 element from a unlimited stream of integers: top3
// generic cases: topk
//
// compiling instruction: g++ --std=c++17 topk.cpp -o topk

#include <fstream>
#include <iostream>
#include <filesystem>

#define MIN_INT -65535

void top3(char* filename)
{
    using namespace std;
    namespace fs = std::filesystem;
    fs::path fp {filename};
    if (!fs::exists(fp)) {
        cout << "file " << filename << " not exist" << endl;
        return;
    }

    ifstream fstrm(filename);

    int top1 = MIN_INT, top2=MIN_INT, top3=MIN_INT;
    int number;
    while (! fstrm.eof()) {
        fstrm >> number;
        if (number > top1) {
            top2 = top1;
            top1 = number;
        }
        else if (number > top2){
            top3 = top2;
            top2 = number;
        }
        else if (number > top3)
            top3 = number;

    }
    cout << top1 << endl << top2 << endl << top3 << endl;
}

void topk(char* filename, int K)
{
    using namespace std;
    namespace fs = std::filesystem;
    fs::path fp {filename};
    if (!fs::exists(fp)) {
        cout << "file " << filename << " not exist" << endl;
        return;
    }

    ifstream fstrm(filename);

//    int top1 = MIN_INT, top2=MIN_INT, top3=MIN_INT;
    int number;
    int arr[K];

    for (int i=0; i<K; i++)
      arr[i] = MIN_INT;

    while (! fstrm.eof()) {

        fstrm >> number;
        int i = K-1;

        if (number < arr[K-1])
            break;

        for (i = K-1; i >= 0; i--) {
            cout << " number = " << number << " i = " << i << endl;     //debug
            if (number < arr[i] )           // arr[i] > number > arr[i+1]
                break;
        }

        if (i < 0)
            i = 0;

        int j = K-1;
        for (j=K-1; j>i; j--) {
            arr[j] = arr[j-1];
        }

        arr[i] = number;

        cout << " i = " << i << " number = " << number << endl;
    }

    fstrm.close();

    for (int i=0; i<K; i++)
     cout << arr[i] << endl;

}

void topk2(char* filename, int K)
{
    using namespace std;
    namespace fs = std::filesystem;
    fs::path fp {filename};
    if (!fs::exists(fp)) {
        cout << "file " << filename << " not exist" << endl;
        return;
    }

    ifstream fstrm(filename);

//    int top1 = MIN_INT, top2=MIN_INT, top3=MIN_INT;
    int number;
    int arr[K];

    for (int i=0; i<K; i++)
      arr[i] = MIN_INT;

    while (! fstrm.eof()) {

        fstrm >> number;
        int i = K-1;

        if (number < arr[K-1])
            break;

        int l =0;
        for (i = 0; i < K; i++) {
            if ( number > arr[i]) {
               l = i;                   //this will fix the uncertainty of value i
               break;
            }
        }

        int j = K-1;
        for (j=K-1; j>l; j--) {
            arr[j] = arr[j-1];
        }

        arr[l] = number;

        cout << " l = " << l << " number = " << number << endl;
    }

    fstrm.close();

    for (int i=0; i<K; i++)
     cout << arr[i] << endl;

}

void topk3(char* filename, int K)
{
    using namespace std;
    namespace fs = std::filesystem;
    fs::path fp {filename};
    if (!fs::exists(fp)) {
        cout << "file " << filename << " not exist" << endl;
        return;
    }

    ifstream fstrm(filename);

//    int top1 = MIN_INT, top2=MIN_INT, top3=MIN_INT;
    int number;
    int arr[K];

    for (int i=0; i<K; i++)
      arr[i] = MIN_INT;

    while (! fstrm.eof()) {

        fstrm >> number;
        if (!fstrm)
          break;

        int i = K-1;

        if (number < arr[K-1])
            break;

        int l =0;
        for (i = 0; i < K; i++) {
            if ( number > arr[i]) {
               l = i;                   //this will fix the uncertainty of value i
               break;
            }
        }

        int j = K-1;
        for (j=K-1; j>l; j--) {
            arr[j] = arr[j-1];
        }

        arr[l] = number;

        cout << " l = " << l << " number = " << number << endl;
    }

    fstrm.close();

    for (int i=0; i<K; i++)
     cout << arr[i] << endl;

}

int main(int argc, char* argv[])
{
    int K = atoi(argv[2]);
    topk3(argv[1], K);
    return 0;
}

//above version still have some bugs. an input of 1, 2, 3, 4, 5, 6 with command top example.txt 9
// will output 6, 6, 5, 4, 3, 2, 1, -65535, -65535.  When we expect 6, 5, 4, 3, 2, 1, -65535, -65535
// an input of 5,4,3,2,1,0 with top example2.txt 9 will output 4,3,2,1,0,0,5, -65535, -65535

// Future modified versions will need to be worked on and put in topk2, topk3, ..., etc
//
// topk2 has less bug than topk, but testing data still shows 6,6,5,4,3,2 for example.txt and 0,5,4,3,2,1 for
// example2.txt.  This is due to the last number get read twice in the stream.
/*
 l = 0 number = 1
 l = 0 number = 2
 l = 0 number = 3
 l = 0 number = 4
 l = 0 number = 5
 l = 0 number = 6
 l = 1 number = 6

 l = 0 number = 5
 l = 1 number = 4
 l = 2 number = 3
 l = 3 number = 2
 l = 4 number = 1
 l = 5 number = 0
 l = 0 number = 0

*/

//
// topk3 fixed above bug. We check if last read is successful or not before using the data we thought we got from the read.
// if it failed, the fstrm  >> number will be invalid but the data in number is still good at its last value;<commit_msg>used INT_MIN and #include <climits to make use of INT_MIN<commit_after>//Author: John Xu. 6/23/2019
//Initial code drafted on 6/21/2019 on a flight back from Plantation, FL
//
//The initial problem was an Amazon phone screen problem: print out the top K number from a given array of integers
//
//Initally the answer should be simple: take each integer from the array and insert into a priority queue and then
//do a loop to pop from this prioroty queue to print out each of the integer
//
//Then later on, it is asked, what if the array is so big, such as comming from a stream, and does not fit into the memory ?
//My immediate response was: once we reached size K for the priority queue, we remove the last element before insert the
//new element into the priority queue, if the new element is larger than the last element of the priority queue.
//
// Above immediate response has following defects:
//
// 1) priority queue does not have a method to access or remove the last element
//    regular queue has push, pop, front, back, size, empty.
//    dequeue has push_back, push_front, pop_back, pop_front, size, empty
//    priority_queue has push, pop, top, size, empty
//
// 2) over use of priority queue may be too slow. Priority queue better used for cases where the data is really used as
//    a queue structure, where processed elements are gone from the storage, where no need to keep the historical top K
//    element.
//
// 3) If we want to keep the historical top K element from a unlimited stream inout, it is more efficient to maintain a
//    dynamic array of size K. Maintain the array sorted from large to small. When new element comes in, if it is smaller
//    than the last one, ignore it. Otherwise, remove the last one, and shift every element one position to next one if that
//    element is smaller than the new one, then insert the new one at the end of the shift.
//

//example of keep top 3 element from a unlimited stream of integers: top3
// generic cases: topk
//
// compiling instruction: g++ --std=c++17 topk.cpp -o topk

#include <fstream>
#include <iostream>
#include <filesystem>

#define MIN_INT -65535

void top3(char* filename)
{
    using namespace std;
    namespace fs = std::filesystem;
    fs::path fp {filename};
    if (!fs::exists(fp)) {
        cout << "file " << filename << " not exist" << endl;
        return;
    }

    ifstream fstrm(filename);

    int top1 = MIN_INT, top2=MIN_INT, top3=MIN_INT;
    int number;
    while (! fstrm.eof()) {
        fstrm >> number;
        if (number > top1) {
            top2 = top1;
            top1 = number;
        }
        else if (number > top2){
            top3 = top2;
            top2 = number;
        }
        else if (number > top3)
            top3 = number;

    }
    cout << top1 << endl << top2 << endl << top3 << endl;
}

void topk(char* filename, int K)
{
    using namespace std;
    namespace fs = std::filesystem;
    fs::path fp {filename};
    if (!fs::exists(fp)) {
        cout << "file " << filename << " not exist" << endl;
        return;
    }

    ifstream fstrm(filename);

//    int top1 = MIN_INT, top2=MIN_INT, top3=MIN_INT;
    int number;
    int arr[K];

    for (int i=0; i<K; i++)
      arr[i] = MIN_INT;

    while (! fstrm.eof()) {

        fstrm >> number;
        int i = K-1;

        if (number < arr[K-1])
            break;

        for (i = K-1; i >= 0; i--) {
            cout << " number = " << number << " i = " << i << endl;     //debug
            if (number < arr[i] )           // arr[i] > number > arr[i+1]
                break;
        }

        if (i < 0)
            i = 0;

        int j = K-1;
        for (j=K-1; j>i; j--) {
            arr[j] = arr[j-1];
        }

        arr[i] = number;

        cout << " i = " << i << " number = " << number << endl;
    }

    fstrm.close();

    for (int i=0; i<K; i++)
     cout << arr[i] << endl;

}

void topk2(char* filename, int K)
{
    using namespace std;
    namespace fs = std::filesystem;
    fs::path fp {filename};
    if (!fs::exists(fp)) {
        cout << "file " << filename << " not exist" << endl;
        return;
    }

    ifstream fstrm(filename);

//    int top1 = MIN_INT, top2=MIN_INT, top3=MIN_INT;
    int number;
    int arr[K];

    for (int i=0; i<K; i++)
      arr[i] = MIN_INT;

    while (! fstrm.eof()) {

        fstrm >> number;
        int i = K-1;

        if (number < arr[K-1])
            break;

        int l =0;
        for (i = 0; i < K; i++) {
            if ( number > arr[i]) {
               l = i;                   //this will fix the uncertainty of value i
               break;
            }
        }

        int j = K-1;
        for (j=K-1; j>l; j--) {
            arr[j] = arr[j-1];
        }

        arr[l] = number;

        cout << " l = " << l << " number = " << number << endl;
    }

    fstrm.close();

    for (int i=0; i<K; i++)
     cout << arr[i] << endl;

}

#include <climits>
void topk3(char* filename, int K)
{
    using namespace std;
    namespace fs = std::filesystem;
    fs::path fp {filename};
    if (!fs::exists(fp)) {
        cout << "file " << filename << " not exist" << endl;
        return;
    }

    ifstream fstrm(filename);

//    int top1 = MIN_INT, top2=MIN_INT, top3=MIN_INT;
    int number;
    int arr[K];

    for (int i=0; i<K; i++)
//      arr[i] = MIN_INT;
      arr[i] = INT_MIN;

    while (! fstrm.eof()) {

        fstrm >> number;
        if (!fstrm)
          break;

        int i = K-1;

        if (number < arr[K-1])
            break;

        int l =0;
        for (i = 0; i < K; i++) {
            if ( number > arr[i]) {
               l = i;                   //this will fix the uncertainty of value i
               break;
            }
        }

        int j = K-1;
        for (j=K-1; j>l; j--) {
            arr[j] = arr[j-1];
        }

        arr[l] = number;

        cout << " l = " << l << " number = " << number << endl;
    }

    fstrm.close();

    for (int i=0; i<K; i++)
     cout << arr[i] << endl;

}

int main(int argc, char* argv[])
{
    int K = atoi(argv[2]);
    topk3(argv[1], K);
    return 0;
}

//above version still have some bugs. an input of 1, 2, 3, 4, 5, 6 with command top example.txt 9
// will output 6, 6, 5, 4, 3, 2, 1, -65535, -65535.  When we expect 6, 5, 4, 3, 2, 1, -65535, -65535
// an input of 5,4,3,2,1,0 with top example2.txt 9 will output 4,3,2,1,0,0,5, -65535, -65535

// Future modified versions will need to be worked on and put in topk2, topk3, ..., etc
//
// topk2 has less bug than topk, but testing data still shows 6,6,5,4,3,2 for example.txt and 0,5,4,3,2,1 for
// example2.txt.  This is due to the last number get read twice in the stream.
/*
 l = 0 number = 1
 l = 0 number = 2
 l = 0 number = 3
 l = 0 number = 4
 l = 0 number = 5
 l = 0 number = 6
 l = 1 number = 6

 l = 0 number = 5
 l = 1 number = 4
 l = 2 number = 3
 l = 3 number = 2
 l = 4 number = 1
 l = 5 number = 0
 l = 0 number = 0

*/

//
// topk3 fixed above bug. We check if last read is successful or not before using the data we thought we got from the read.
// if it failed, the fstrm  >> number will be invalid but the data in number is still good at its last value;<|endoftext|>


<commit_before>#include "computing_data.h"
#include "bart_builder.h"

#include <deal.II/fe/fe_update_flags.h>

// XSections::XSections (Materials& material)
//     :
//     sigt(material.GetSigT()),
//     inv_sigt(material.GetInvSigT()),
//     q(material.GetQ()),
//     q_per_ster(material.GetQPerSter()),
//     is_material_fissile(material.GetFissileIDMap()),
//     nu_sigf(material.GetNuSigf()),
//     sigs(material.GetSigS()),
//     sigs_per_ster(material.GetSigSPerSter()),
//     fiss_transfer(material.GetFissTransfer()),
//     fiss_transfer_per_ster(material.GetFissTransferPerSter()) {}

XSections::XSections (MaterialBase& material)
    :
    sigt(material.GetSigT()),
    inv_sigt(material.GetInvSigT()),
    q(material.GetQ()),
    q_per_ster(material.GetQPerSter()),
    is_material_fissile(material.GetFissileIDMap()),
    nu_sigf(material.GetNuSigF()),
    sigs(material.GetSigS()),
    sigs_per_ster(material.GetSigSPerSter()),
    fiss_transfer(material.GetChiNuSigF()),
    fiss_transfer_per_ster(material.GetChiNuSigFPerSter())
{}

template <int dim>
FundamentalData<dim>::FundamentalData (dealii::ParameterHandler &prm,
    dealii::Triangulation<dim> &tria)
    :
    pcout(std::cout,
        (dealii::Utilities::MPI::this_mpi_process(MPI_COMM_WORLD)==0)),
    //material(prm),
    mesh(prm),
    mat_vec(std::shared_ptr<MatrixVector> (new MatrixVector())),
    //xsec(std::shared_ptr<XSections> (new XSections(material))),
    fe_data(prm),
    dof_handler(tria) {
  MaterialProtobuf material{prm};
  xsec = std::make_shared<XSections>(material);
  bbuilders::BuildAQ<dim>(prm, aq);
  aq->MakeAQ();
}

template <int dim>
FundamentalData<dim>::~FundamentalData () {}

template <int dim>
FEData<dim>::FEData (const dealii::ParameterHandler &prm) {
  bbuilders::BuildFESpaces(prm, fe);
  const dealii::UpdateFlags update_flags =
      dealii::update_values | dealii::update_gradients |
      dealii::update_quadrature_points |
      dealii::update_JxW_values;
  const dealii::UpdateFlags update_face_flags = dealii::update_normal_vectors |
      dealii::update_values | dealii::update_gradients |
      dealii::update_quadrature_points |
      dealii::update_JxW_values;

  for (auto &f : fe) {
    // f.first is the equation name, f.second is the correspoding FiniteElement
    // object
    // TODO: all fe use the same finite element for now.
    if (f.first!="nda" && f.first!="tg_nda")
      discretization[f.first] = prm.get("ho spatial discretization");
    else
      discretization[f.first] = prm.get("nda spatial discretization");
    p_order[f.first] = prm.get_integer("finite element polynomial degree");
    q_rule[f.first] = std::shared_ptr<dealii::QGauss<dim>>(
        new dealii::QGauss<dim>(p_order[f.first]+1));
    qf_rule[f.first] = std::shared_ptr<dealii::QGauss<dim-1>>(
        new dealii::QGauss<dim-1>(p_order[f.first]+1));

    fv[f.first] = std::shared_ptr<dealii::FEValues<dim>> (
        new dealii::FEValues<dim> (*f.second, *q_rule[f.first], update_flags));

    fvf[f.first] = std::shared_ptr<dealii::FEFaceValues<dim>> (
        new dealii::FEFaceValues<dim>(
            *f.second, *qf_rule[f.first], update_face_flags));

    fvf_nei[f.first] = std::shared_ptr<dealii::FEFaceValues<dim>> (
        new dealii::FEFaceValues<dim>(
            *f.second, *qf_rule[f.first], update_face_flags));

    dofs_per_cell[f.first] = fe[f.first]->dofs_per_cell;
    n_q[f.first] = q_rule[f.first]->size();
    n_qf[f.first] = qf_rule[f.first]->size();

    local_dof_indices[f.first].resize (dofs_per_cell[f.first]);
    neigh_dof_indices[f.first].resize (dofs_per_cell[f.first]);

    if (f.first=="nda") {
      // corrections related objects
      q_rule["corr"] = std::shared_ptr<dealii::QGauss<dim>>(
          new dealii::QGauss<dim>(p_order[f.first]+3));
      qf_rule["corr"] = std::shared_ptr<dealii::QGauss<dim-1>>(
          new dealii::QGauss<dim-1>(p_order[f.first]+3));

      fv["corr"] = std::shared_ptr<dealii::FEValues<dim>> (
          new dealii::FEValues<dim> (*f.second, *q_rule["corr"], update_face_flags));

      fvf["corr"] = std::shared_ptr<dealii::FEFaceValues<dim>> (
          new dealii::FEFaceValues<dim>(
              *f.second, *qf_rule["corr"], update_face_flags));

      fvf_nei["corr"] = std::shared_ptr<dealii::FEFaceValues<dim>> (
          new dealii::FEFaceValues<dim>(
              *f.second, *qf_rule["corr"], update_face_flags));

      n_q["corr"] = q_rule["corr"]->size();
      n_qf["corr"] = qf_rule["corr"]->size();
    }
  }
}

template <int dim>
FEData<dim>::~FEData() {}

template struct FundamentalData<1>;
template struct FundamentalData<2>;
template struct FundamentalData<3>;

template struct FEData<1>;
template struct FEData<2>;
template struct FEData<3>;
<commit_msg>replace BuildAQ call with new CreateAQ factory in FundamentalData class<commit_after>#include "computing_data.h"
#include "bart_builder.h"

#include <deal.II/fe/fe_update_flags.h>

XSections::XSections (MaterialBase& material)
    :
    sigt(material.GetSigT()),
    inv_sigt(material.GetInvSigT()),
    q(material.GetQ()),
    q_per_ster(material.GetQPerSter()),
    is_material_fissile(material.GetFissileIDMap()),
    nu_sigf(material.GetNuSigF()),
    sigs(material.GetSigS()),
    sigs_per_ster(material.GetSigSPerSter()),
    fiss_transfer(material.GetChiNuSigF()),
    fiss_transfer_per_ster(material.GetChiNuSigFPerSter())
{}

template <int dim>
FundamentalData<dim>::FundamentalData (dealii::ParameterHandler &prm,
    dealii::Triangulation<dim> &tria)
    :
    pcout(std::cout,
        (dealii::Utilities::MPI::this_mpi_process(MPI_COMM_WORLD)==0)),
    aq(AQBase<dim>::CreateAQ(prm)),
    //material(prm),
    mesh(prm),
    mat_vec(std::shared_ptr<MatrixVector> (new MatrixVector())),
    //xsec(std::shared_ptr<XSections> (new XSections(material))),
    fe_data(prm),
    dof_handler(tria) {
  MaterialProtobuf material{prm};
  xsec = std::make_shared<XSections>(material);
  aq->MakeAQ();
}

template <int dim>
FundamentalData<dim>::~FundamentalData () {}

template <int dim>
FEData<dim>::FEData (const dealii::ParameterHandler &prm) {
  bbuilders::BuildFESpaces(prm, fe);
  const dealii::UpdateFlags update_flags =
      dealii::update_values | dealii::update_gradients |
      dealii::update_quadrature_points |
      dealii::update_JxW_values;
  const dealii::UpdateFlags update_face_flags = dealii::update_normal_vectors |
      dealii::update_values | dealii::update_gradients |
      dealii::update_quadrature_points |
      dealii::update_JxW_values;

  for (auto &f : fe) {
    // f.first is the equation name, f.second is the correspoding FiniteElement
    // object
    // TODO: all fe use the same finite element for now.
    if (f.first!="nda" && f.first!="tg_nda")
      discretization[f.first] = prm.get("ho spatial discretization");
    else
      discretization[f.first] = prm.get("nda spatial discretization");
    p_order[f.first] = prm.get_integer("finite element polynomial degree");
    q_rule[f.first] = std::shared_ptr<dealii::QGauss<dim>>(
        new dealii::QGauss<dim>(p_order[f.first]+1));
    qf_rule[f.first] = std::shared_ptr<dealii::QGauss<dim-1>>(
        new dealii::QGauss<dim-1>(p_order[f.first]+1));

    fv[f.first] = std::shared_ptr<dealii::FEValues<dim>> (
        new dealii::FEValues<dim> (*f.second, *q_rule[f.first], update_flags));

    fvf[f.first] = std::shared_ptr<dealii::FEFaceValues<dim>> (
        new dealii::FEFaceValues<dim>(
            *f.second, *qf_rule[f.first], update_face_flags));

    fvf_nei[f.first] = std::shared_ptr<dealii::FEFaceValues<dim>> (
        new dealii::FEFaceValues<dim>(
            *f.second, *qf_rule[f.first], update_face_flags));

    dofs_per_cell[f.first] = fe[f.first]->dofs_per_cell;
    n_q[f.first] = q_rule[f.first]->size();
    n_qf[f.first] = qf_rule[f.first]->size();

    local_dof_indices[f.first].resize (dofs_per_cell[f.first]);
    neigh_dof_indices[f.first].resize (dofs_per_cell[f.first]);

    if (f.first=="nda") {
      // corrections related objects
      q_rule["corr"] = std::shared_ptr<dealii::QGauss<dim>>(
          new dealii::QGauss<dim>(p_order[f.first]+3));
      qf_rule["corr"] = std::shared_ptr<dealii::QGauss<dim-1>>(
          new dealii::QGauss<dim-1>(p_order[f.first]+3));

      fv["corr"] = std::shared_ptr<dealii::FEValues<dim>> (
          new dealii::FEValues<dim> (*f.second, *q_rule["corr"], update_face_flags));

      fvf["corr"] = std::shared_ptr<dealii::FEFaceValues<dim>> (
          new dealii::FEFaceValues<dim>(
              *f.second, *qf_rule["corr"], update_face_flags));

      fvf_nei["corr"] = std::shared_ptr<dealii::FEFaceValues<dim>> (
          new dealii::FEFaceValues<dim>(
              *f.second, *qf_rule["corr"], update_face_flags));

      n_q["corr"] = q_rule["corr"]->size();
      n_qf["corr"] = qf_rule["corr"]->size();
    }
  }
}

template <int dim>
FEData<dim>::~FEData() {}

template struct FundamentalData<1>;
template struct FundamentalData<2>;
template struct FundamentalData<3>;

template struct FEData<1>;
template struct FEData<2>;
template struct FEData<3>;
<|endoftext|>


<commit_before>/** \file   SystemdUtil.cc
 *  \brief  Helper functions to use with systemd
 *  \author Mario Trojan (mario.trojan@uni-tuebingen.de)
 *
 *  \copyright 2018,2019 Universitätsbibliothek Tübingen.  All rights reserved.
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
#include "SystemdUtil.h"
#include "ExecUtil.h"
#include "FileUtil.h"
#include "RegexMatcher.h"
#include "StringUtil.h"
#include "util.h"

const std::string SYSTEMCTL_EXECUTABLE("systemctl");
const std::string SYSTEMD_SERVICE_DIRECTORY("/etc/systemd/system/");

bool SystemdUtil::IsAvailable() {
    static const bool is_available(ExecUtil::GetOriginalCommandNameFromPID(1) == "systemd");
    return is_available;
}

void SystemdUtil::Reload() {
    ExecUtil::ExecOrDie(ExecUtil::Which(SYSTEMCTL_EXECUTABLE), { "daemon-reload"});
}

void SystemdUtil::DisableUnit(const std::string &unit) {
    ExecUtil::ExecOrDie(ExecUtil::Which(SYSTEMCTL_EXECUTABLE), { "disable", unit });
}

void SystemdUtil::EnableUnit(const std::string &unit) {
    ExecUtil::ExecOrDie(ExecUtil::Which(SYSTEMCTL_EXECUTABLE), { "enable", unit });
}

void SystemdUtil::InstallUnit(const std::string &service_file_path) {
    std::string service_file_dirname, service_file_basename;
    FileUtil::DirnameAndBasename(service_file_path, &service_file_dirname, &service_file_basename);

    ExecUtil::ExecOrDie(ExecUtil::Which("mkdir"), { "-p", SYSTEMD_SERVICE_DIRECTORY });
    FileUtil::CopyOrDie(service_file_path, SYSTEMD_SERVICE_DIRECTORY + service_file_basename);
    SystemdUtil::Reload();
}

bool SystemdUtil::IsUnitAvailable(const std::string &unit) {
    std::string out, err;
    ExecUtil::ExecSubcommandAndCaptureStdoutAndStderr(ExecUtil::Which(SYSTEMCTL_EXECUTABLE), { "--all", "list-unit-files" }, &out, &err);
    return RegexMatcher::Matched("^" + unit + "\\.service", out, RegexMatcher::MULTILINE);
}

bool SystemdUtil::IsUnitEnabled(const std::string &unit) {
    std::string out, err;
    ExecUtil::ExecSubcommandAndCaptureStdoutAndStderr(ExecUtil::Which(SYSTEMCTL_EXECUTABLE), { "is-enabled", unit }, &out, &err);
    return StringUtil::StartsWith(out, "enabled", /* ignore_case */ true);
}

bool SystemdUtil::IsUnitRunning(const std::string &unit) {
    std::string out, err;
    ExecUtil::ExecSubcommandAndCaptureStdoutAndStderr(ExecUtil::Which(SYSTEMCTL_EXECUTABLE), { "status", unit }, &out, &err);
    return RegexMatcher::Matched("(running)", out);
}

void SystemdUtil::RestartUnit(const std::string &unit) {
    ExecUtil::ExecOrDie(ExecUtil::Which(SYSTEMCTL_EXECUTABLE), { "restart", unit });
}

void SystemdUtil::StartUnit(const std::string &unit) {
    ExecUtil::ExecOrDie(ExecUtil::Which(SYSTEMCTL_EXECUTABLE), { "start", unit });
}
<commit_msg>improved SystemdUtil::IsAvailable<commit_after>/** \file   SystemdUtil.cc
 *  \brief  Helper functions to use with systemd
 *  \author Mario Trojan (mario.trojan@uni-tuebingen.de)
 *
 *  \copyright 2018,2019 Universitätsbibliothek Tübingen.  All rights reserved.
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
#include "SystemdUtil.h"
#include "ExecUtil.h"
#include "FileUtil.h"
#include "RegexMatcher.h"
#include "StringUtil.h"
#include "util.h"

const std::string SYSTEMCTL_EXECUTABLE("systemctl");
const std::string SYSTEMD_SERVICE_DIRECTORY("/etc/systemd/system/");

bool SystemdUtil::IsAvailable() {
    // Docker: systemd not running at all (+ not installed)
    // CentOS: systemd running with pid 1
    // Ubuntu: systemd running with random pid
    static const std::unordered_set<unsigned> systemd_pids(ExecUtil::FindActivePrograms("systemd"));
    return systemd_pids.size() > 0;
}

void SystemdUtil::Reload() {
    ExecUtil::ExecOrDie(ExecUtil::Which(SYSTEMCTL_EXECUTABLE), { "daemon-reload"});
}

void SystemdUtil::DisableUnit(const std::string &unit) {
    ExecUtil::ExecOrDie(ExecUtil::Which(SYSTEMCTL_EXECUTABLE), { "disable", unit });
}

void SystemdUtil::EnableUnit(const std::string &unit) {
    ExecUtil::ExecOrDie(ExecUtil::Which(SYSTEMCTL_EXECUTABLE), { "enable", unit });
}

void SystemdUtil::InstallUnit(const std::string &service_file_path) {
    std::string service_file_dirname, service_file_basename;
    FileUtil::DirnameAndBasename(service_file_path, &service_file_dirname, &service_file_basename);

    ExecUtil::ExecOrDie(ExecUtil::Which("mkdir"), { "-p", SYSTEMD_SERVICE_DIRECTORY });
    FileUtil::CopyOrDie(service_file_path, SYSTEMD_SERVICE_DIRECTORY + service_file_basename);
    SystemdUtil::Reload();
}

bool SystemdUtil::IsUnitAvailable(const std::string &unit) {
    std::string out, err;
    ExecUtil::ExecSubcommandAndCaptureStdoutAndStderr(ExecUtil::Which(SYSTEMCTL_EXECUTABLE), { "--all", "list-unit-files" }, &out, &err);
    return RegexMatcher::Matched("^" + unit + "\\.service", out, RegexMatcher::MULTILINE);
}

bool SystemdUtil::IsUnitEnabled(const std::string &unit) {
    std::string out, err;
    ExecUtil::ExecSubcommandAndCaptureStdoutAndStderr(ExecUtil::Which(SYSTEMCTL_EXECUTABLE), { "is-enabled", unit }, &out, &err);
    return StringUtil::StartsWith(out, "enabled", /* ignore_case */ true);
}

bool SystemdUtil::IsUnitRunning(const std::string &unit) {
    std::string out, err;
    ExecUtil::ExecSubcommandAndCaptureStdoutAndStderr(ExecUtil::Which(SYSTEMCTL_EXECUTABLE), { "status", unit }, &out, &err);
    return RegexMatcher::Matched("(running)", out);
}

void SystemdUtil::RestartUnit(const std::string &unit) {
    ExecUtil::ExecOrDie(ExecUtil::Which(SYSTEMCTL_EXECUTABLE), { "restart", unit });
}

void SystemdUtil::StartUnit(const std::string &unit) {
    ExecUtil::ExecOrDie(ExecUtil::Which(SYSTEMCTL_EXECUTABLE), { "start", unit });
}
<|endoftext|>


<commit_before>/** \file   SystemdUtil.cc
 *  \brief  Helper functions to use with systemd
 *  \author Mario Trojan (mario.trojan@uni-tuebingen.de)
 *
 *  \copyright 2018,2019 Universitätsbibliothek Tübingen.  All rights reserved.
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
#include "SystemdUtil.h"
#include "ExecUtil.h"
#include "FileUtil.h"
#include "RegexMatcher.h"
#include "StringUtil.h"
#include "util.h"

const std::string SYSTEMCTL_EXECUTABLE("systemctl");
const std::string SYSTEMD_SERVICE_DIRECTORY("/etc/systemd/system/");

bool SystemdUtil::IsAvailable() {
    return ExecUtil::GetOriginalCommandNameFromPID(1) == "systemd";
}

void SystemdUtil::Reload() {
    ExecUtil::ExecOrDie(ExecUtil::Which(SYSTEMCTL_EXECUTABLE), { "daemon-reload"});
}

void SystemdUtil::DisableUnit(const std::string &unit) {
    ExecUtil::ExecOrDie(ExecUtil::Which(SYSTEMCTL_EXECUTABLE), { "disable", unit });
}

void SystemdUtil::EnableUnit(const std::string &unit) {
    ExecUtil::ExecOrDie(ExecUtil::Which(SYSTEMCTL_EXECUTABLE), { "enable", unit });
}

void SystemdUtil::InstallUnit(const std::string &service_file_path) {
    std::string service_file_dirname, service_file_basename;
    FileUtil::DirnameAndBasename(service_file_path, &service_file_dirname, &service_file_basename);

    ExecUtil::ExecOrDie(ExecUtil::Which("mkdir"), { "-p", SYSTEMD_SERVICE_DIRECTORY });
    FileUtil::CopyOrDie(service_file_path, SYSTEMD_SERVICE_DIRECTORY + service_file_basename);
    SystemdUtil::Reload();
}

bool SystemdUtil::IsUnitAvailable(const std::string &unit) {
    std::string out, err;
    ExecUtil::ExecSubcommandAndCaptureStdoutAndStderr(ExecUtil::Which(SYSTEMCTL_EXECUTABLE), { "--all", "list-unit-files" }, &out, &err);
    return RegexMatcher::Matched("^" + unit + "\\.service", out, RegexMatcher::MULTILINE);
}

bool SystemdUtil::IsUnitEnabled(const std::string &unit) {
    std::string out, err;
    ExecUtil::ExecSubcommandAndCaptureStdoutAndStderr(ExecUtil::Which(SYSTEMCTL_EXECUTABLE), { "is-enabled", unit }, &out, &err);
    return StringUtil::StartsWith(out, "enabled", /* ignore_case */ true);
}

bool SystemdUtil::IsUnitRunning(const std::string &unit) {
    std::string out, err;
    ExecUtil::ExecSubcommandAndCaptureStdoutAndStderr(ExecUtil::Which(SYSTEMCTL_EXECUTABLE), { "status", unit }, &out, &err);
    return RegexMatcher::Matched("(running)", out);
}

void SystemdUtil::RestartUnit(const std::string &unit) {
    ExecUtil::ExecOrDie(ExecUtil::Which(SYSTEMCTL_EXECUTABLE), { "restart", unit });
}

void SystemdUtil::StartUnit(const std::string &unit) {
    ExecUtil::ExecOrDie(ExecUtil::Which(SYSTEMCTL_EXECUTABLE), { "start", unit });
}
<commit_msg>Cache the sucker!<commit_after>/** \file   SystemdUtil.cc
 *  \brief  Helper functions to use with systemd
 *  \author Mario Trojan (mario.trojan@uni-tuebingen.de)
 *
 *  \copyright 2018,2019 Universitätsbibliothek Tübingen.  All rights reserved.
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
#include "SystemdUtil.h"
#include "ExecUtil.h"
#include "FileUtil.h"
#include "RegexMatcher.h"
#include "StringUtil.h"
#include "util.h"

const std::string SYSTEMCTL_EXECUTABLE("systemctl");
const std::string SYSTEMD_SERVICE_DIRECTORY("/etc/systemd/system/");

bool SystemdUtil::IsAvailable() {
    static const bool is_available(ExecUtil::GetOriginalCommandNameFromPID(1) == "systemd");
    return is_available;
}

void SystemdUtil::Reload() {
    ExecUtil::ExecOrDie(ExecUtil::Which(SYSTEMCTL_EXECUTABLE), { "daemon-reload"});
}

void SystemdUtil::DisableUnit(const std::string &unit) {
    ExecUtil::ExecOrDie(ExecUtil::Which(SYSTEMCTL_EXECUTABLE), { "disable", unit });
}

void SystemdUtil::EnableUnit(const std::string &unit) {
    ExecUtil::ExecOrDie(ExecUtil::Which(SYSTEMCTL_EXECUTABLE), { "enable", unit });
}

void SystemdUtil::InstallUnit(const std::string &service_file_path) {
    std::string service_file_dirname, service_file_basename;
    FileUtil::DirnameAndBasename(service_file_path, &service_file_dirname, &service_file_basename);

    ExecUtil::ExecOrDie(ExecUtil::Which("mkdir"), { "-p", SYSTEMD_SERVICE_DIRECTORY });
    FileUtil::CopyOrDie(service_file_path, SYSTEMD_SERVICE_DIRECTORY + service_file_basename);
    SystemdUtil::Reload();
}

bool SystemdUtil::IsUnitAvailable(const std::string &unit) {
    std::string out, err;
    ExecUtil::ExecSubcommandAndCaptureStdoutAndStderr(ExecUtil::Which(SYSTEMCTL_EXECUTABLE), { "--all", "list-unit-files" }, &out, &err);
    return RegexMatcher::Matched("^" + unit + "\\.service", out, RegexMatcher::MULTILINE);
}

bool SystemdUtil::IsUnitEnabled(const std::string &unit) {
    std::string out, err;
    ExecUtil::ExecSubcommandAndCaptureStdoutAndStderr(ExecUtil::Which(SYSTEMCTL_EXECUTABLE), { "is-enabled", unit }, &out, &err);
    return StringUtil::StartsWith(out, "enabled", /* ignore_case */ true);
}

bool SystemdUtil::IsUnitRunning(const std::string &unit) {
    std::string out, err;
    ExecUtil::ExecSubcommandAndCaptureStdoutAndStderr(ExecUtil::Which(SYSTEMCTL_EXECUTABLE), { "status", unit }, &out, &err);
    return RegexMatcher::Matched("(running)", out);
}

void SystemdUtil::RestartUnit(const std::string &unit) {
    ExecUtil::ExecOrDie(ExecUtil::Which(SYSTEMCTL_EXECUTABLE), { "restart", unit });
}

void SystemdUtil::StartUnit(const std::string &unit) {
    ExecUtil::ExecOrDie(ExecUtil::Which(SYSTEMCTL_EXECUTABLE), { "start", unit });
}
<|endoftext|>


<commit_before>#include <iostream>
#include <unistd.h>
#include <netinet/tcp.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netdb.h>
#include <pthread.h>
#include <errno.h>

#include <json/writer.h>
#include <json/reader.h>
#include <json/value.h>

#include <grids/define.h>
#include <grids/protocol.h>

namespace Grids {
  void *runEventLoopThreadEntryPoint(void *arg) {
    Protocol *gp = (Protocol *)arg;
    gp->runEventLoop();
  }
  /*
  void *dispatchEventEntryPoint(void *arg) {
    Protocol *gp = (Protocol *)arg;
    gp->dispatchEvent();
  }
  */
  Protocol::Protocol() {
    sock = 0;
    finished = 0;
    pthread_mutex_init(&finishedMutex, NULL);
    eventLoopThread = (pthread_t)NULL;
  }

  bool Protocol::connectToNode(const char *address) {
    // look up host
    struct hostent *hp;
    struct sockaddr_in addr;
    struct timeval timeout;
    int on = 1;

    timeout.tv_sec = 1;
    timeout.tv_usec = 0;

    if ((hp = gethostbyname(address)) == NULL) {
      herror("gethostbyname");
      return 0;
    }

    bcopy(hp->h_addr, &addr.sin_addr, hp->h_length);
    addr.sin_port = htons(GRIDS_PORT);
    addr.sin_family = AF_INET;

    sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);

    if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (const char *)&on, sizeof(int)) != 0) {
      std::cerr << "Could not setsockopt TCP_NODELAY: " << strerror(errno) << "\n";
      return -1;
    }
    if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(struct timeval)) != 0) {
      std::cerr << "Could not setsockopt SO_RCVTIMEO: " << strerror(errno) << "\n";
      return -1;
    }

    if (connect(sock, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)) == -1) {
      return 0;
    }

    // hooray we are connnected! initialize protocol
    sendProtocolInitiationString();

    return 1;
  }

  void Protocol::sendProtocolInitiationString() {
    protocolWrite("==Grids/1.0/JSON");
  }

  int Protocol::protocolWrite(const char *str) {
    uint32_t len = strlen(str);
    uint32_t len_net = htonl(len);

    unsigned int outstr_len = len + 4;
    char *outstr = (char *)malloc(outstr_len);

    memcpy(outstr, &len_net, 4);
    memcpy((outstr + 4), str, len);

    int ret = write(sock, outstr, outstr_len);
    free(outstr);

    return ret;
  }

  void Protocol::closeConnection() {
    ::shutdown(sock, SHUT_RDWR);
    ::close(sock);
  }

  std::string Protocol::stringifyMap(gridsmap_t *m) {
    Json::FastWriter *writer = new Json::FastWriter();
    Json::Value root = mapToJsonValue(m);
    return writer->write(root);
  }

  void Protocol::sendRequest(std::string evt) {
    sendRequest(evt, NULL);
  }

  void Protocol::sendRequest(std::string evt, gridsmap_t *args) {
    if (evt.empty())
      return;

    if (args == NULL) {
      args = new gridsmap_t();
    }

    const static std::string methodkey = "_method";
    (*args)[methodkey] = evt;
    std::string msg = stringifyMap(args);

    protocolWrite(msg.c_str());
  }

  Json::Value Protocol::mapToJsonValue(gridsmap_t *m) {
    giterator mapIterator;

    Json::Value jsonVal;

    for(mapIterator = m->begin();
        mapIterator != m->end();
        mapIterator++) {

      gridskey_t key = mapIterator->first;
      gridsval_t val = mapIterator->second;

      jsonVal[key] = val;
    }

    return jsonVal;
  }

  void Protocol::setEventCallback(gevent_callback_t cb, void *userData) { eventCallback = cb; eventCallbackUserData = userData; }

  void Protocol::runEventLoop() {
    int bytesRead;
    uint32_t incomingLength;
    char *buf;
    char *bufIncoming;

    while (! isFinished() && sock) {
      // read in 4 byte length of message
      bytesRead = read(sock, &incomingLength, 4);
      incomingLength = ntohl(incomingLength);

      //std::cout << "bytesRead: " << bytesRead << " incoming: " << incomingLength << "\n";

      if (bytesRead == -1) {
        if (errno == EAGAIN) {
          // nothing to read, do it again
          continue;
        }

        // uh oh, socket read error
        std::cerr << "Socket read error: " << strerror(errno) << "\n";
        break;
      }

      if (bytesRead != 4) {
        std::cerr << "read zero bytes\n";
        // wtf? try reading again
        continue;
      }

      if (incomingLength > 1024 * 1024 * 1024) {
        // not going to read in more than a gig, f that
        std::cerr << "Got incoming message size: " << incomingLength << ". Skipping\n";
        continue;
      }

      // TODO: run in seperate thread

      // allocate space for incoming message + null byte
      buf = (char *)malloc(incomingLength + 1);

      //std::cout << "incoming: " << incomingLength << "\n";
      uint32_t bytesRemaining = incomingLength;
      bufIncoming = buf;

      do {
        bytesRead = read(sock, bufIncoming, bytesRemaining);

        if (bytesRead > 0) {
          bytesRemaining -= bytesRead;
          bufIncoming += bytesRead;
        }

        //std::cout << "read: " << bytesRead << " remaining: " << bytesRemaining << "\n";

      } while ((bytesRead > 0 || errno != EAGAIN) && bytesRemaining && ! isFinished());
      buf[incomingLength] = '\0';

      if (bytesRead == -1) {
        // o snap read error
        std::cerr << "Socket read error: " << bytesRead << "\n";
        free(buf);
        break;
      }

      if (bytesRead == 0) {
        // not chill
        std::cerr << "Didn't read any data when expecting message of " << incomingLength << " bytes\n";
        free(buf);
        continue;
      }

      if (bytesRead != incomingLength) {
        std::cerr << "Only read " << bytesRead << " bytes when expecting message of "
                  << incomingLength << " bytes\n";
        free(buf);
        continue;
      }

      // TODO: run in seperate thread
      std::string msg = buf;
      handleMessage(msg);

      free(buf);
    }

    std::cout << "ended read thread\n";
  }

  void Protocol::handleMessage(std::string &msg) {
    std::cout << "Got message \"" << msg << "\"\n";
    if (msg.size() < 2) return; // obv. bogus

    if (msg.find("==", 0, 2) == 0) {
      // protocol initiation message
    } else if (msg.find("--", 0, 2) == 0) {
      // encrypted protocol message
    } else {
      // assume this is json for now
      Json::Value root = parseJson(msg);

      // FIXME: this is slow and lame
      gridsmap_t rootMap = jsonToMap(root);

      Event *evt = new Event(rootMap["_method"], rootMap);
      eventCallback(this, evt, eventCallbackUserData);
      delete evt;
    }
  }

  gridsmap_t Protocol::jsonToMap(Json::Value &root) {
    // ghetto, should fix in the future
    Json::Value::Members memberList = root.getMemberNames();
    gridsmap_t outMap;

    for( memberList = memberList.begin(); memberList != memberList.end(); memberList++ ) {
      outMap[*memberList] = root[*memberList];
    }

    return outMap;
  }

  Json::Value Protocol::parseJson(std::string &msg) {
    Json::Value root;
    Json::Reader reader;

    if (reader.parse(msg, root))
      return root;

    std::cerr << "Could not parse JSON: " << msg << "\n";
    return Json::Value(0);
  }

  int Protocol::runEventLoopThreaded() {
    return pthread_create(&eventLoopThread, NULL, runEventLoopThreadEntryPoint, this);
  }

  void Protocol::stopEventLoopThread() {
    pthread_mutex_lock(&finishedMutex);
    finished = 1;
    pthread_mutex_unlock(&finishedMutex);

    if (eventLoopThread)
      pthread_join(eventLoopThread, NULL);
  }

  short Protocol::isFinished() {
    int isFinished;
    pthread_mutex_lock(&finishedMutex);
    isFinished = finished;
    pthread_mutex_unlock(&finishedMutex);

    return isFinished;
  }
}
<commit_msg>convert json value to map of strings for now. slow and ghetto but will work<commit_after>#include <iostream>
#include <sstream>
#include <unistd.h>
#include <netinet/tcp.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netdb.h>
#include <pthread.h>
#include <errno.h>

#include <json/writer.h>
#include <json/reader.h>
#include <json/value.h>

#include <grids/define.h>
#include <grids/protocol.h>

namespace Grids {
  void *runEventLoopThreadEntryPoint(void *arg) {
    Protocol *gp = (Protocol *)arg;
    gp->runEventLoop();
  }
  /*
  void *dispatchEventEntryPoint(void *arg) {
    Protocol *gp = (Protocol *)arg;
    gp->dispatchEvent();
  }
  */
  Protocol::Protocol() {
    sock = 0;
    finished = 0;
    pthread_mutex_init(&finishedMutex, NULL);
    eventLoopThread = (pthread_t)NULL;
  }

  bool Protocol::connectToNode(const char *address) {
    // look up host
    struct hostent *hp;
    struct sockaddr_in addr;
    struct timeval timeout;
    int on = 1;

    timeout.tv_sec = 1;
    timeout.tv_usec = 0;

    if ((hp = gethostbyname(address)) == NULL) {
      herror("gethostbyname");
      return 0;
    }

    bcopy(hp->h_addr, &addr.sin_addr, hp->h_length);
    addr.sin_port = htons(GRIDS_PORT);
    addr.sin_family = AF_INET;

    sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);

    if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (const char *)&on, sizeof(int)) != 0) {
      std::cerr << "Could not setsockopt TCP_NODELAY: " << strerror(errno) << "\n";
      return -1;
    }
    if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(struct timeval)) != 0) {
      std::cerr << "Could not setsockopt SO_RCVTIMEO: " << strerror(errno) << "\n";
      return -1;
    }

    if (connect(sock, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)) == -1) {
      return 0;
    }

    // hooray we are connnected! initialize protocol
    sendProtocolInitiationString();

    return 1;
  }

  void Protocol::sendProtocolInitiationString() {
    protocolWrite("==Grids/1.0/JSON");
  }

  int Protocol::protocolWrite(const char *str) {
    uint32_t len = strlen(str);
    uint32_t len_net = htonl(len);

    unsigned int outstr_len = len + 4;
    char *outstr = (char *)malloc(outstr_len);

    memcpy(outstr, &len_net, 4);
    memcpy((outstr + 4), str, len);

    int ret = write(sock, outstr, outstr_len);
    free(outstr);

    return ret;
  }

  void Protocol::closeConnection() {
    ::shutdown(sock, SHUT_RDWR);
    ::close(sock);
  }

  std::string Protocol::stringifyMap(gridsmap_t *m) {
    Json::FastWriter *writer = new Json::FastWriter();
    Json::Value root = mapToJsonValue(m);
    return writer->write(root);
  }

  void Protocol::sendRequest(std::string evt) {
    sendRequest(evt, NULL);
  }

  void Protocol::sendRequest(std::string evt, gridsmap_t *args) {
    if (evt.empty())
      return;

    if (args == NULL) {
      args = new gridsmap_t();
    }

    const static std::string methodkey = "_method";
    (*args)[methodkey] = evt;
    std::string msg = stringifyMap(args);

    protocolWrite(msg.c_str());
  }

  Json::Value Protocol::mapToJsonValue(gridsmap_t *m) {
    giterator mapIterator;

    Json::Value jsonVal;

    for(mapIterator = m->begin();
        mapIterator != m->end();
        mapIterator++) {

      gridskey_t key = mapIterator->first;
      gridsval_t val = mapIterator->second;

      jsonVal[key] = val;
    }

    return jsonVal;
  }

  void Protocol::setEventCallback(gevent_callback_t cb, void *userData) { eventCallback = cb; eventCallbackUserData = userData; }

  void Protocol::runEventLoop() {
    int bytesRead;
    uint32_t incomingLength;
    char *buf;
    char *bufIncoming;

    while (! isFinished() && sock) {
      // read in 4 byte length of message
      bytesRead = read(sock, &incomingLength, 4);
      incomingLength = ntohl(incomingLength);

      //std::cout << "bytesRead: " << bytesRead << " incoming: " << incomingLength << "\n";

      if (bytesRead == -1) {
        if (errno == EAGAIN) {
          // nothing to read, do it again
          continue;
        }

        // uh oh, socket read error
        std::cerr << "Socket read error: " << strerror(errno) << "\n";
        break;
      }

      if (bytesRead != 4) {
        std::cerr << "read zero bytes\n";
        // wtf? try reading again
        continue;
      }

      if (incomingLength > 1024 * 1024 * 1024) {
        // not going to read in more than a gig, f that
        std::cerr << "Got incoming message size: " << incomingLength << ". Skipping\n";
        continue;
      }

      // TODO: run in seperate thread

      // allocate space for incoming message + null byte
      buf = (char *)malloc(incomingLength + 1);

      //std::cout << "incoming: " << incomingLength << "\n";
      uint32_t bytesRemaining = incomingLength;
      bufIncoming = buf;

      do {
        bytesRead = read(sock, bufIncoming, bytesRemaining);

        if (bytesRead > 0) {
          bytesRemaining -= bytesRead;
          bufIncoming += bytesRead;
        }

        //std::cout << "read: " << bytesRead << " remaining: " << bytesRemaining << "\n";

      } while ((bytesRead > 0 || errno != EAGAIN) && bytesRemaining && ! isFinished());
      buf[incomingLength] = '\0';

      if (bytesRead == -1) {
        // o snap read error
        std::cerr << "Socket read error: " << bytesRead << "\n";
        free(buf);
        break;
      }

      if (bytesRead == 0) {
        // not chill
        std::cerr << "Didn't read any data when expecting message of " << incomingLength << " bytes\n";
        free(buf);
        continue;
      }

      if (bytesRead != incomingLength) {
        std::cerr << "Only read " << bytesRead << " bytes when expecting message of "
                  << incomingLength << " bytes\n";
        free(buf);
        continue;
      }

      // TODO: run in seperate thread
      std::string msg = buf;
      handleMessage(msg);

      free(buf);
    }

    std::cout << "ended read thread\n";
  }

  void Protocol::handleMessage(std::string &msg) {
    std::cout << "Got message \"" << msg << "\"\n";
    if (msg.size() < 2) return; // obv. bogus

    if (msg.find("==", 0, 2) == 0) {
      // protocol initiation message
    } else if (msg.find("--", 0, 2) == 0) {
      // encrypted protocol message
    } else {
      // assume this is json for now
      Json::Value root = parseJson(msg);

      // FIXME: this is slow and lame
      gridsmap_t rootMap = jsonToMap(root);

      Event *evt = new Event(rootMap["_method"], rootMap);
      eventCallback(this, evt, eventCallbackUserData);
      delete evt;
    }
  }

  gridsmap_t Protocol::jsonToMap(Json::Value &root) {
    // ghetto, should fix in the future
    Json::Value::Members memberList = root.getMemberNames();
    std::vector<std::string>::iterator iter;

    gridsmap_t outMap;

    for (iter = memberList.begin(); iter != memberList.end(); iter++) {
      Json::Value val = root[*iter];

      std::stringstream outVal;
      outVal << val;

      outMap[*iter] = outVal.str();
    }

    return outMap;
  }

  Json::Value Protocol::parseJson(std::string &msg) {
    Json::Value root;
    Json::Reader reader;

    if (reader.parse(msg, root))
      return root;

    std::cerr << "Could not parse JSON: " << msg << "\n";
    return Json::Value(0);
  }

  int Protocol::runEventLoopThreaded() {
    return pthread_create(&eventLoopThread, NULL, runEventLoopThreadEntryPoint, this);
  }

  void Protocol::stopEventLoopThread() {
    pthread_mutex_lock(&finishedMutex);
    finished = 1;
    pthread_mutex_unlock(&finishedMutex);

    if (eventLoopThread)
      pthread_join(eventLoopThread, NULL);
  }

  short Protocol::isFinished() {
    int isFinished;
    pthread_mutex_lock(&finishedMutex);
    isFinished = finished;
    pthread_mutex_unlock(&finishedMutex);

    return isFinished;
  }
}
<|endoftext|>