<commit_before>/* -*- coding: utf-8; mode: c++; tab-width: 3; indent-tabs-mode: nil -*-

Copyright 2016-2017 Raffaello D. Di Napoli

This file is part of Lofty.

Lofty is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General
Public License as published by the Free Software Foundation, either version 3 of the License, or (at your
option) any later version.

Lofty 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 Lesser General Public License for
more details.

You should have received a copy of the GNU Lesser General Public License along with Lofty. If not, see
<http://www.gnu.org/licenses/>.
------------------------------------------------------------------------------------------------------------*/

#include <lofty.hxx>
#include <lofty/collections/vector.hxx>
#include <lofty/text/parsers/dynamic.hxx>

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

namespace lofty { namespace text { namespace parsers {

namespace {

//! Backtracking data structure.
struct backtrack {
   //! Pointer to the state the backtrack refers to.
   dynamic::state const * state;
   //! true if *state accepted the input, or false otherwise.
   bool accepted;

   /*! Constructor.

   @param state_
      Pointer to the state the backtrack refers to.
   @param accepted_
      true if *state accepted the input, or false otherwise.
   */
   backtrack(dynamic::state const * state_, bool accepted_) :
      state(state_),
      accepted(accepted_) {
   }
};

//! Used to track the acceptance of repetition states.
struct repetition {
   //! Pointer to the related repetition state.
   dynamic::state const * state;
   //! Number of times the repetition has occurred.
   unsigned count;

   /*! Constructor.

   @param state_
      Pointer to the related repetition state.
   */
   explicit repetition(dynamic::state const * state_) :
      state(state_),
      count(0) {
   }
};

} //namespace

struct dynamic::match::capture_node {
   //! Pointer to the related capture_begin state.
   struct state const * state;
   //! Pointer to the parent (containing) capture. Only nullptr for capture 0.
   capture_node * parent;
   //! Owning pointer to the first nested capture, if any.
   _std::unique_ptr<capture_node> first_nested;
   //! Non-owning pointer to the last nested capture, if any.
   capture_node * last_nested;
   //! Non-owning pointer to the previous same-level capture, if any.
   capture_node * prev_sibling;
   //! Owning pointer to the next same-level capture, if any.
   _std::unique_ptr<capture_node> next_sibling;
   //! Offset of the start of the capture.
   std::size_t begin;
   //! Offset of the end of the capture.
   std::size_t end;

   /*! Constructor.

   @param state_
      Pointer to the related capture_begin state.
   */
   explicit capture_node(struct state const * state_) :
      state(state_),
      parent(nullptr),
      last_nested(nullptr),
      prev_sibling(nullptr) {
   }

   //! Destructor.
   ~capture_node() {
      // last_nested is the only non-owning forward pointer, so it must be updated manually.
      if (parent && parent->last_nested == this) {
         parent->last_nested = prev_sibling;
      }
   }

   /*! Adds a new capture_node at the end of the nested capture nodes list.

   @param state_
      Pointer to the related capture_begin state.
   */
   capture_node * append_nested(struct state const * state_) {
      _std::unique_ptr<capture_node> ret_owner(new capture_node(state_));
      auto ret = ret_owner.get();
      if (last_nested) {
         last_nested->next_sibling = _std::move(ret_owner);
         ret->prev_sibling = last_nested;
      } else {
         first_nested = _std::move(ret_owner);
         last_nested = ret;
      }
      ret->parent = this;
      return ret;
   }
};

dynamic::match::match() {
}

dynamic::match::match(match && src) :
   captures_buffer(_std::move(src.captures_buffer)),
   capture0(_std::move(src.capture0)) {
}

dynamic::match::~match() {
}

dynamic::dynamic() :
   initial_state(nullptr) {
}

dynamic::dynamic(dynamic && src) :
   states_list(_std::move(src.states_list)),
   initial_state(src.initial_state) {
   src.initial_state = nullptr;
}

dynamic::~dynamic() {
}

dynamic::state * dynamic::create_begin_state() {
   return create_uninitialized_state(state_type::begin);
}

dynamic::state * dynamic::create_capture_begin_state() {
   return create_uninitialized_state(state_type::capture_begin);
}

dynamic::state * dynamic::create_capture_end_state() {
   return create_uninitialized_state(state_type::capture_end);
}

dynamic::state * dynamic::create_code_point_state(char32_t cp) {
   state * ret = create_uninitialized_state(state_type::range);
   ret->u.range.first_cp = cp;
   ret->u.range.last_cp = cp;
   return ret;
}

dynamic::state * dynamic::create_code_point_range_state(char32_t first_cp, char32_t last_cp) {
   state * ret = create_uninitialized_state(state_type::range);
   ret->u.range.first_cp = first_cp;
   ret->u.range.last_cp = last_cp;
   return ret;
}

dynamic::state * dynamic::create_end_state() {
   return create_uninitialized_state(state_type::end);
}

dynamic::state * dynamic::create_repetition_state(
   state const * repeated_state, std::uint16_t min, std::uint16_t max /*= 0*/
) {
   state * ret = create_uninitialized_state(state_type::repetition);
   ret->u.repetition.repeated_state = repeated_state;
   ret->u.repetition.min = min;
   ret->u.repetition.max = max;
   ret->u.repetition.greedy = true;
   return ret;
}

dynamic::state * dynamic::create_uninitialized_state(state_type type) {
   states_list.push_back(state());
   state * ret = static_cast<state *>(&states_list.back());
   ret->type = type.base();
   ret->next = nullptr;
   ret->alternative = nullptr;
   return ret;
}

dynamic::match dynamic::run(str const & s) const {
   io::text::str_istream istream(external_buffer, &s);
   return run(&istream);
}

dynamic::match dynamic::run(io::text::istream * istream) const {
   LOFTY_TRACE_FUNC(this, istream);

   match ret;
   auto curr_state = initial_state;
   // Cache this condition to quickly determine whether we’re allowed to skip input code points.
   bool begin_anchor = (curr_state && curr_state->type == state_type::begin && !curr_state->alternative);
   // Setup the two sources of code points: a history and a peek buffer from the input stream.
   str & history_buf = ret.captures_buffer, peek_buf = istream->peek_chars(1);
   auto history_begin_itr(history_buf.cbegin()), history_end(history_buf.cend());
   auto history_itr(history_begin_itr), peek_itr(peek_buf.cbegin()), peek_end(peek_buf.cend());

   ret.capture0.reset(new match::capture_node(curr_state));
   auto curr_capture = ret.capture0.get();

   // TODO: change these two variables to use collections::stack once that’s available.
   collections::vector<backtrack> backtracking_stack;
   /* Stack of repetition counters. A new counter is pushed when a new repetition is started, and popped when
   that repetition is a) matched max times or b) backtracked over. */
   collections::vector<repetition> reps_stack;

   bool accepted = (curr_state == nullptr);
   while (curr_state) {
      state const * next_state = curr_state->next;
      switch (curr_state->type) {
         case state_type::range: {
            // Get a code point from either history or the peek buffer.
            char32_t cp;
            bool save_peeked_cp_to_history;
            if (history_itr != history_end) {
               cp = *history_itr;
               save_peeked_cp_to_history = false;
            } else {
               if (peek_itr == peek_end) {
                  istream->consume_chars(peek_buf.size_in_chars());
                  peek_buf = istream->peek_chars(1);
                  peek_itr = peek_buf.cbegin();
                  peek_end = peek_buf.cend();
                  if (peek_itr == peek_end) {
                     // Run out of code points.
                     accepted = false;
                     break;
                  }
               }
               cp = *peek_itr;
               save_peeked_cp_to_history = true;
            }

            accepted = (cp >= curr_state->u.range.first_cp && cp <= curr_state->u.range.last_cp);
            if (accepted) {
               if (save_peeked_cp_to_history) {
                  history_buf += cp;
                  ++history_end;
                  ++peek_itr;
               }
               ++history_itr;
            }
            break;
         }

         case state_type::repetition: {
            repetition * rep;
            if (reps_stack && (rep = &reps_stack.front())->state == curr_state) {
               ++rep->count;
            } else {
               // New repetition: save it on the stack and begin counting.
               reps_stack.push_back(repetition(curr_state));
               rep = &reps_stack.back();
            }
            if (
               curr_state->u.repetition.max == 0 ||
               rep->count <= static_cast<unsigned>(curr_state->u.repetition.max)
            ) {
               // Repetitions within [min, max] are accepting.
               accepted = (rep->count >= static_cast<unsigned>(curr_state->u.repetition.min));
               // Try one more repetition.
               next_state = curr_state->u.repetition.repeated_state;
            } else {
               // Repeated max times; pop the stack and move on to the next state.
               accepted = true;
               reps_stack.pop_back();
            }
            goto skip_acceptance_test;
         }

         case state_type::begin:
            accepted = (history_itr == history_begin_itr);
            break;

         case state_type::end:
            if (history_itr == history_end && peek_itr == peek_end) {
               /* We consumed history and peek buffer, but “end” really means end, so also check that the
               stream is empty. */
               /* TODO: this might be redundant, since other code point consumers in this function always do
               this after consuming a code point. */
               istream->consume_chars(peek_buf.size_in_chars());
               peek_buf = istream->peek_chars(1);
               accepted = !peek_buf;
            } else {
               accepted = false;
            }
            break;

         case state_type::capture_begin:
            // Nest this capture into the currently-open capture.
            curr_capture = curr_capture->append_nested(curr_state);
            curr_capture->begin = history_itr.char_index();
            goto skip_acceptance_test;

         case state_type::capture_end:
            curr_capture->end = history_itr.char_index();
            // This capture is over; resume its parent.
            curr_capture = curr_capture->parent;
            goto skip_acceptance_test;
      }

      if (accepted) {
skip_acceptance_test:
         if (!next_state) {
            // No more states; this means that the input was accepted.
            break;
         }
         // Still one or more states to check; this means that we can’t accept the input just yet.
         backtracking_stack.push_back(backtrack(curr_state, accepted));
         curr_state = next_state;
      } else {
         // Consider the next alternative of the current state or a prior one.
         curr_state = curr_state->alternative;
         while (!curr_state && backtracking_stack) {
            auto backtrack(backtracking_stack.pop_back());
            switch (backtrack.state->type) {
               case state_type::range:
                  /* If the state we’re rolling back accepted (and consumed) a code point, it must’ve saved
                  it in history_buf, so recover it from there. */
                  if (backtrack.accepted) {
                     --history_itr;
                  }
                  break;

               case state_type::repetition:
                  // If we’re backtracking the current top-of-the-stack repetition, pop it out of it.
                  if (reps_stack && reps_stack.back().state == backtrack.state) {
                     reps_stack.pop_back();
                  }
                  /* This is a repetition’s Nth occurrence; decide what to do depending on whether N is in the
                  acceptable range. */
                  if (!backtrack.accepted) {
                     // Move on to the alternative, like non-repeating states.
                     break;
                  } else if (backtrack.state->next) {
                     // Move past the repetition, ignoring any alternatives.
                     curr_state = backtrack.state->next;
                     continue;
                  } else {
                     // If there was no following state, the input is accepted.
                     accepted = true;
                     goto break_outer_while;
                  }

               case state_type::capture_begin: {
                  // Discard *curr_capture (by resetting its owner’s pointer) and move back to its parent.
                  auto parent_capture = curr_capture->parent;
                  if (auto prev_sibling = curr_capture->prev_sibling) {
                     prev_sibling->next_sibling.reset();
                  } else {
                     parent_capture->first_nested.reset();
                  }
                  curr_capture = parent_capture;
                  break;
               }

               case state_type::capture_end:
                  // Re-enter the last (closed) nested capture.
                  curr_capture = curr_capture->last_nested;
                  break;

               default:
                  // No special action needed.
                  break;
            }
            // Not a repetition, or Nth occurrence with N not in the acceptable range.
            curr_state = backtrack.state->alternative;
         }
         /* If we run out of alternatives and can’t backtrack any further, and the pattern is not anchored,
         we’re allowed to move one code point to history and try the whole pattern again from the initial
         state. */
         if (!curr_state && !begin_anchor) {
            if (history_itr == history_end) {
               if (peek_itr == peek_end) {
                  istream->consume_chars(peek_buf.size_in_chars());
                  peek_buf = istream->peek_chars(1);
                  if (!peek_buf) {
                     // Run out of code points.
                     break;
                  }
                  peek_itr = peek_buf.cbegin();
                  peek_end = peek_buf.cend();
               }
               history_buf += *peek_itr++;
               ++history_end;
            }
            ++history_itr;
            curr_state = initial_state;
         }
      }
   }
break_outer_while:
   if (!accepted) {
      ret.capture0.reset();
   }
   return _std::move(ret);
}

}}} //namespace lofty::text::parsers
<commit_msg>Fix another likely bug due to front/back confusion<commit_after>/* -*- coding: utf-8; mode: c++; tab-width: 3; indent-tabs-mode: nil -*-

Copyright 2016-2017 Raffaello D. Di Napoli

This file is part of Lofty.

Lofty is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General
Public License as published by the Free Software Foundation, either version 3 of the License, or (at your
option) any later version.

Lofty 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 Lesser General Public License for
more details.

You should have received a copy of the GNU Lesser General Public License along with Lofty. If not, see
<http://www.gnu.org/licenses/>.
------------------------------------------------------------------------------------------------------------*/

#include <lofty.hxx>
#include <lofty/collections/vector.hxx>
#include <lofty/text/parsers/dynamic.hxx>

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

namespace lofty { namespace text { namespace parsers {

namespace {

//! Backtracking data structure.
struct backtrack {
   //! Pointer to the state the backtrack refers to.
   dynamic::state const * state;
   //! true if *state accepted the input, or false otherwise.
   bool accepted;

   /*! Constructor.

   @param state_
      Pointer to the state the backtrack refers to.
   @param accepted_
      true if *state accepted the input, or false otherwise.
   */
   backtrack(dynamic::state const * state_, bool accepted_) :
      state(state_),
      accepted(accepted_) {
   }
};

//! Used to track the acceptance of repetition states.
struct repetition {
   //! Pointer to the related repetition state.
   dynamic::state const * state;
   //! Number of times the repetition has occurred.
   unsigned count;

   /*! Constructor.

   @param state_
      Pointer to the related repetition state.
   */
   explicit repetition(dynamic::state const * state_) :
      state(state_),
      count(0) {
   }
};

} //namespace

struct dynamic::match::capture_node {
   //! Pointer to the related capture_begin state.
   struct state const * state;
   //! Pointer to the parent (containing) capture. Only nullptr for capture 0.
   capture_node * parent;
   //! Owning pointer to the first nested capture, if any.
   _std::unique_ptr<capture_node> first_nested;
   //! Non-owning pointer to the last nested capture, if any.
   capture_node * last_nested;
   //! Non-owning pointer to the previous same-level capture, if any.
   capture_node * prev_sibling;
   //! Owning pointer to the next same-level capture, if any.
   _std::unique_ptr<capture_node> next_sibling;
   //! Offset of the start of the capture.
   std::size_t begin;
   //! Offset of the end of the capture.
   std::size_t end;

   /*! Constructor.

   @param state_
      Pointer to the related capture_begin state.
   */
   explicit capture_node(struct state const * state_) :
      state(state_),
      parent(nullptr),
      last_nested(nullptr),
      prev_sibling(nullptr) {
   }

   //! Destructor.
   ~capture_node() {
      // last_nested is the only non-owning forward pointer, so it must be updated manually.
      if (parent && parent->last_nested == this) {
         parent->last_nested = prev_sibling;
      }
   }

   /*! Adds a new capture_node at the end of the nested capture nodes list.

   @param state_
      Pointer to the related capture_begin state.
   */
   capture_node * append_nested(struct state const * state_) {
      _std::unique_ptr<capture_node> ret_owner(new capture_node(state_));
      auto ret = ret_owner.get();
      if (last_nested) {
         last_nested->next_sibling = _std::move(ret_owner);
         ret->prev_sibling = last_nested;
      } else {
         first_nested = _std::move(ret_owner);
         last_nested = ret;
      }
      ret->parent = this;
      return ret;
   }
};

dynamic::match::match() {
}

dynamic::match::match(match && src) :
   captures_buffer(_std::move(src.captures_buffer)),
   capture0(_std::move(src.capture0)) {
}

dynamic::match::~match() {
}

dynamic::dynamic() :
   initial_state(nullptr) {
}

dynamic::dynamic(dynamic && src) :
   states_list(_std::move(src.states_list)),
   initial_state(src.initial_state) {
   src.initial_state = nullptr;
}

dynamic::~dynamic() {
}

dynamic::state * dynamic::create_begin_state() {
   return create_uninitialized_state(state_type::begin);
}

dynamic::state * dynamic::create_capture_begin_state() {
   return create_uninitialized_state(state_type::capture_begin);
}

dynamic::state * dynamic::create_capture_end_state() {
   return create_uninitialized_state(state_type::capture_end);
}

dynamic::state * dynamic::create_code_point_state(char32_t cp) {
   state * ret = create_uninitialized_state(state_type::range);
   ret->u.range.first_cp = cp;
   ret->u.range.last_cp = cp;
   return ret;
}

dynamic::state * dynamic::create_code_point_range_state(char32_t first_cp, char32_t last_cp) {
   state * ret = create_uninitialized_state(state_type::range);
   ret->u.range.first_cp = first_cp;
   ret->u.range.last_cp = last_cp;
   return ret;
}

dynamic::state * dynamic::create_end_state() {
   return create_uninitialized_state(state_type::end);
}

dynamic::state * dynamic::create_repetition_state(
   state const * repeated_state, std::uint16_t min, std::uint16_t max /*= 0*/
) {
   state * ret = create_uninitialized_state(state_type::repetition);
   ret->u.repetition.repeated_state = repeated_state;
   ret->u.repetition.min = min;
   ret->u.repetition.max = max;
   ret->u.repetition.greedy = true;
   return ret;
}

dynamic::state * dynamic::create_uninitialized_state(state_type type) {
   states_list.push_back(state());
   state * ret = static_cast<state *>(&states_list.back());
   ret->type = type.base();
   ret->next = nullptr;
   ret->alternative = nullptr;
   return ret;
}

dynamic::match dynamic::run(str const & s) const {
   io::text::str_istream istream(external_buffer, &s);
   return run(&istream);
}

dynamic::match dynamic::run(io::text::istream * istream) const {
   LOFTY_TRACE_FUNC(this, istream);

   match ret;
   auto curr_state = initial_state;
   // Cache this condition to quickly determine whether we’re allowed to skip input code points.
   bool begin_anchor = (curr_state && curr_state->type == state_type::begin && !curr_state->alternative);
   // Setup the two sources of code points: a history and a peek buffer from the input stream.
   str & history_buf = ret.captures_buffer, peek_buf = istream->peek_chars(1);
   auto history_begin_itr(history_buf.cbegin()), history_end(history_buf.cend());
   auto history_itr(history_begin_itr), peek_itr(peek_buf.cbegin()), peek_end(peek_buf.cend());

   ret.capture0.reset(new match::capture_node(curr_state));
   auto curr_capture = ret.capture0.get();

   // TODO: change these two variables to use collections::stack once that’s available.
   collections::vector<backtrack> backtracking_stack;
   /* Stack of repetition counters. A new counter is pushed when a new repetition is started, and popped when
   that repetition is a) matched max times or b) backtracked over. */
   collections::vector<repetition> reps_stack;

   bool accepted = (curr_state == nullptr);
   while (curr_state) {
      state const * next_state = curr_state->next;
      switch (curr_state->type) {
         case state_type::range: {
            // Get a code point from either history or the peek buffer.
            char32_t cp;
            bool save_peeked_cp_to_history;
            if (history_itr != history_end) {
               cp = *history_itr;
               save_peeked_cp_to_history = false;
            } else {
               if (peek_itr == peek_end) {
                  istream->consume_chars(peek_buf.size_in_chars());
                  peek_buf = istream->peek_chars(1);
                  peek_itr = peek_buf.cbegin();
                  peek_end = peek_buf.cend();
                  if (peek_itr == peek_end) {
                     // Run out of code points.
                     accepted = false;
                     break;
                  }
               }
               cp = *peek_itr;
               save_peeked_cp_to_history = true;
            }

            accepted = (cp >= curr_state->u.range.first_cp && cp <= curr_state->u.range.last_cp);
            if (accepted) {
               if (save_peeked_cp_to_history) {
                  history_buf += cp;
                  ++history_end;
                  ++peek_itr;
               }
               ++history_itr;
            }
            break;
         }

         case state_type::repetition: {
            repetition * rep;
            if (reps_stack && (rep = &reps_stack.back())->state == curr_state) {
               ++rep->count;
            } else {
               // New repetition: save it on the stack and begin counting.
               reps_stack.push_back(repetition(curr_state));
               rep = &reps_stack.back();
            }
            if (
               curr_state->u.repetition.max == 0 ||
               rep->count <= static_cast<unsigned>(curr_state->u.repetition.max)
            ) {
               // Repetitions within [min, max] are accepting.
               accepted = (rep->count >= static_cast<unsigned>(curr_state->u.repetition.min));
               // Try one more repetition.
               next_state = curr_state->u.repetition.repeated_state;
            } else {
               // Repeated max times; pop the stack and move on to the next state.
               accepted = true;
               reps_stack.pop_back();
            }
            goto skip_acceptance_test;
         }

         case state_type::begin:
            accepted = (history_itr == history_begin_itr);
            break;

         case state_type::end:
            if (history_itr == history_end && peek_itr == peek_end) {
               /* We consumed history and peek buffer, but “end” really means end, so also check that the
               stream is empty. */
               /* TODO: this might be redundant, since other code point consumers in this function always do
               this after consuming a code point. */
               istream->consume_chars(peek_buf.size_in_chars());
               peek_buf = istream->peek_chars(1);
               accepted = !peek_buf;
            } else {
               accepted = false;
            }
            break;

         case state_type::capture_begin:
            // Nest this capture into the currently-open capture.
            curr_capture = curr_capture->append_nested(curr_state);
            curr_capture->begin = history_itr.char_index();
            goto skip_acceptance_test;

         case state_type::capture_end:
            curr_capture->end = history_itr.char_index();
            // This capture is over; resume its parent.
            curr_capture = curr_capture->parent;
            goto skip_acceptance_test;
      }

      if (accepted) {
skip_acceptance_test:
         if (!next_state) {
            // No more states; this means that the input was accepted.
            break;
         }
         // Still one or more states to check; this means that we can’t accept the input just yet.
         backtracking_stack.push_back(backtrack(curr_state, accepted));
         curr_state = next_state;
      } else {
         // Consider the next alternative of the current state or a prior one.
         curr_state = curr_state->alternative;
         while (!curr_state && backtracking_stack) {
            auto backtrack(backtracking_stack.pop_back());
            switch (backtrack.state->type) {
               case state_type::range:
                  /* If the state we’re rolling back accepted (and consumed) a code point, it must’ve saved
                  it in history_buf, so recover it from there. */
                  if (backtrack.accepted) {
                     --history_itr;
                  }
                  break;

               case state_type::repetition:
                  // If we’re backtracking the current top-of-the-stack repetition, pop it out of it.
                  if (reps_stack && reps_stack.back().state == backtrack.state) {
                     reps_stack.pop_back();
                  }
                  /* This is a repetition’s Nth occurrence; decide what to do depending on whether N is in the
                  acceptable range. */
                  if (!backtrack.accepted) {
                     // Move on to the alternative, like non-repeating states.
                     break;
                  } else if (backtrack.state->next) {
                     // Move past the repetition, ignoring any alternatives.
                     curr_state = backtrack.state->next;
                     continue;
                  } else {
                     // If there was no following state, the input is accepted.
                     accepted = true;
                     goto break_outer_while;
                  }

               case state_type::capture_begin: {
                  // Discard *curr_capture (by resetting its owner’s pointer) and move back to its parent.
                  auto parent_capture = curr_capture->parent;
                  if (auto prev_sibling = curr_capture->prev_sibling) {
                     prev_sibling->next_sibling.reset();
                  } else {
                     parent_capture->first_nested.reset();
                  }
                  curr_capture = parent_capture;
                  break;
               }

               case state_type::capture_end:
                  // Re-enter the last (closed) nested capture.
                  curr_capture = curr_capture->last_nested;
                  break;

               default:
                  // No special action needed.
                  break;
            }
            // Not a repetition, or Nth occurrence with N not in the acceptable range.
            curr_state = backtrack.state->alternative;
         }
         /* If we run out of alternatives and can’t backtrack any further, and the pattern is not anchored,
         we’re allowed to move one code point to history and try the whole pattern again from the initial
         state. */
         if (!curr_state && !begin_anchor) {
            if (history_itr == history_end) {
               if (peek_itr == peek_end) {
                  istream->consume_chars(peek_buf.size_in_chars());
                  peek_buf = istream->peek_chars(1);
                  if (!peek_buf) {
                     // Run out of code points.
                     break;
                  }
                  peek_itr = peek_buf.cbegin();
                  peek_end = peek_buf.cend();
               }
               history_buf += *peek_itr++;
               ++history_end;
            }
            ++history_itr;
            curr_state = initial_state;
         }
      }
   }
break_outer_while:
   if (!accepted) {
      ret.capture0.reset();
   }
   return _std::move(ret);
}

}}} //namespace lofty::text::parsers
<|endoftext|>


<commit_before>#include "text_node.h"

using namespace liven;

int text_node::get_offset(horizontal_align_type align) {
	switch (align) {
		case horizontal_align_type::left:
		return 0;
		case horizontal_align_type::center:
		return (text.size() * char_width) / 2;
		case horizontal_align_type::right:
		return (text.size() * char_width);
	}
}

point<2> text_node::get_anchor() {
	auto loc = get_location();
	return point<2>(loc.x + get_offset(h_align),loc.y);
}

void text_node::set_anchor(point<2> new_anchor) {
	point<3> delta = point<3>(new_anchor - get_anchor());
	shift(delta);
}

void text_node::align_left() {
	set_h_align(horizontal_align_type::left);
}

void text_node::align_center() {
	set_h_align(horizontal_align_type::center);
}

void text_node::align_right() {
	set_h_align(horizontal_align_type::right);
}

void text_node::set_h_align(horizontal_align_type align) {
	auto loc = get_location();
	set_location(get_anchor().x - get_offset(align), loc.y, loc.z);
}

std::string text_node::get_text() const {
	return text;
}

void text_node::set_text(std::string text_) {
	text = text_;
}

<commit_msg>fix alignment issues<commit_after>#include "text_node.h"

using namespace liven;

int text_node::get_offset(horizontal_align_type align) {
	switch (align) {
		case horizontal_align_type::left:
		return 0;
		case horizontal_align_type::center:
		return (text.size() * char_width) / 2;
		case horizontal_align_type::right:
		return (text.size() * char_width);
	}
}

point<2> text_node::get_anchor() {
	auto loc = get_location();
	return point<2>(loc.x + get_offset(h_align),loc.y);
}

void text_node::set_anchor(point<2> new_anchor) {
	point<3> delta = point<3>(new_anchor - get_anchor());
	shift(delta);
}

void text_node::align_left() {
	set_h_align(horizontal_align_type::left);
}

void text_node::align_center() {
	set_h_align(horizontal_align_type::center);
}

void text_node::align_right() {
	set_h_align(horizontal_align_type::right);
}

void text_node::set_h_align(horizontal_align_type align) {
	auto loc = get_location();
	set_location(get_anchor().x - get_offset(align), loc.y, loc.z);
	h_align = align;
}

std::string text_node::get_text() const {
	return text;
}

void text_node::set_text(std::string text_) {
	auto loc = get_location();
	auto current_anchor = get_anchor();
	text = text_;
	set_location(current_anchor.x - get_offset(h_align), loc.y, loc.z);
}

<|endoftext|>


<commit_before>#include "Customs/CentralLightScript1.hpp"
#include "Customs/ForestActivatorScript2.hpp"
#include "Globals/EngineGlobals.hpp"

const int quantityFrame = 13;
const int activedTrue = 1;
const int framePerSecond = 9;

ForestActivatorScript2::ForestActivatorScript2(GameObject *owner) : Script(owner) {

}

/**
    @brief that function starts the activion of the forest 2. Create the animation,
    position, animator, controller, input and the map.
*/
void ForestActivatorScript2::Start() {
    /*
        Create animations, define position, animator through gameobject.
        Defines map and sets its zoom proportion vector using width and height.
        Sets the joystick.
    */
    CreateAnimations();
    position = GetOwner()->GetPosition();
    animator = (Animator *)GetOwner()->GetComponent("Animator");
    input = InputSystem::GetInstance();
    gamecontroller = input->GetGameController(0);
    GetOwner()->SetZoomProportion(Vector(0, 0));
    auto map = SceneManager::GetInstance()->GetScene("Gameplay")->GetGameObject("Map");
    // Check for map, and if exists, sets its map on a vector.
    if (map) {
        GetOwner()->SetZoomProportion(Vector(map->originalWidth/GetOwner()->originalWidth,map->originalHeight/GetOwner()->originalHeight));
    }
}

/**
    @brief that function create the animations for the forest. Create the image of
    the forest and the animation of the forest.
*/
void ForestActivatorScript2::CreateAnimations(){
    // Instantiating forest activator Sprite image and its position.
    auto forestactivatorSprite = new Image("assets/forestactivator.png", 0, 0,832, 64);

    // Instantiating forest activator animation by gameobject components, and setting its image and frame.
    auto forestactivatorAnimation = new Animation(GetOwner(), forestactivatorSprite);
    for (int counter = 0; counter < quantityFrame; counter++) {
        forestactivatorAnimation->AddFrame(new Frame(counter * 64, 0, 64, 64));
    }

    // Instantiating forest activator animation2, by gameobject, and setting its image and animation.
    auto forestactivatorAnimation2 = new Animation(GetOwner(), forestactivatorSprite);
    forestactivatorAnimation2->AddFrame(new Frame(12 * 64, 0, 64, 64));

    // Instantiating animator, setting its animation and frames per second.
    auto forestactivatorAnimator = new Animator(GetOwner());
    forestactivatorAnimation->SetFramesPerSecond(framePerSecond);
    //forestactivatorAnimation2->SetFramesPerSecond(1);
    forestactivatorAnimator->AddAnimation("FOREST ACTIVATOR ANIMATION", forestactivatorAnimation);
    forestactivatorAnimator->AddAnimation("FOREST ACTIVATOR ANIMATION2", forestactivatorAnimation2);
}

/**
    @brief that function updates the components of the forest two.
*/
void ForestActivatorScript2::ComponentUpdate() {
    // Check the animator's state, and ifs true, play animation and activate it.
    if (!animator->IsPlaying("FOREST ACTIVATOR ANIMATION") && activate==0 && runned==false) {
        animator->PlayAnimation("FOREST ACTIVATOR ANIMATION");
        activate = activedTrue;
        runned = true;
    }

    // Check if runned and something diferent of animator is playing and active the animattion of the forest.
    if (runned && !animator->IsPlaying("FOREST ACTIVATOR ANIMATION")) {
        animator->PlayAnimation("FOREST ACTIVATOR ANIMATION2");
    }

    // Checking script running state, and ifs true, activate the script.
    if (runned) {
        auto script = (CentralLightScript1*)SceneManager::GetInstance()->
                       GetCurrentScene()->GetGameObject("CENTRAL LIGHT 1")->
                       GetComponent("CentralLightScript1");
        script->Activate();
    }
}

/**
    @brief that function fixs the components updates of the forest two.
*/
void ForestActivatorScript2::FixedComponentUpdate() {

}
<commit_msg>Corrigi erro onde a luz do mapa central começava acessa<commit_after>#include "Customs/CentralLightScript1.hpp"
#include "Customs/ForestActivatorScript2.hpp"
#include "Globals/EngineGlobals.hpp"

const int quantityFrame = 13;
const int framePerSecond = 9;

ForestActivatorScript2::ForestActivatorScript2(GameObject *owner) : Script(owner) {

}

/**
    @brief that function starts the activion of the forest 2. Create the animation,
    position, animator, controller, input and the map.
*/
void ForestActivatorScript2::Start() {
    /*
        Create animations, define position, animator through gameobject.
        Defines map and sets its zoom proportion vector using width and height.
        Sets the joystick.
    */
    CreateAnimations();
    position = GetOwner()->GetPosition();
    animator = (Animator *)GetOwner()->GetComponent("Animator");
    input = InputSystem::GetInstance();
    gamecontroller = input->GetGameController(0);
    GetOwner()->SetZoomProportion(Vector(0, 0));
    auto map = SceneManager::GetInstance()->GetScene("Gameplay")->GetGameObject("Map");
    // Check for map, and if exists, sets its map on a vector.
    if (map) {
        GetOwner()->SetZoomProportion(Vector(map->originalWidth/GetOwner()->originalWidth,map->originalHeight/GetOwner()->originalHeight));
    }
}

/**
    @brief that function create the animations for the forest. Create the image of
    the forest and the animation of the forest.
*/
void ForestActivatorScript2::CreateAnimations(){
    // Instantiating forest activator Sprite image and its position.
    auto forestactivatorSprite = new Image("assets/forestactivator.png", 0, 0,832, 64);

    // Instantiating forest activator animation by gameobject components, and setting its image and frame.
    auto forestactivatorAnimation = new Animation(GetOwner(), forestactivatorSprite);
    for (int counter = 0; counter < quantityFrame; counter++) {
        forestactivatorAnimation->AddFrame(new Frame(counter * 64, 0, 64, 64));
    }

    // Instantiating forest activator animation2, by gameobject, and setting its image and animation.
    auto forestactivatorAnimation2 = new Animation(GetOwner(), forestactivatorSprite);
    forestactivatorAnimation2->AddFrame(new Frame(12 * 64, 0, 64, 64));

    // Instantiating animator, setting its animation and frames per second.
    auto forestactivatorAnimator = new Animator(GetOwner());
    forestactivatorAnimation->SetFramesPerSecond(framePerSecond);
    //forestactivatorAnimation2->SetFramesPerSecond(1);
    forestactivatorAnimator->AddAnimation("FOREST ACTIVATOR ANIMATION", forestactivatorAnimation);
    forestactivatorAnimator->AddAnimation("FOREST ACTIVATOR ANIMATION2", forestactivatorAnimation2);
}

/**
    @brief that function updates the components of the forest two.
*/
void ForestActivatorScript2::ComponentUpdate() {
    // Check the animator's state, and ifs true, play animation and activate it.
    if (!animator->IsPlaying("FOREST ACTIVATOR ANIMATION") && activate==0 && runned==false) {
        animator->PlayAnimation("FOREST ACTIVATOR ANIMATION");
        activate=1;
        runned = true;
    }

    // Check if runned and something diferent of animator is playing and active the animattion of the forest.
    if (runned && !animator->IsPlaying("FOREST ACTIVATOR ANIMATION")) {
        animator->PlayAnimation("FOREST ACTIVATOR ANIMATION2");
    }

    // Checking script running state, and ifs true, activate the script.
    if (runned) {
        auto script = (CentralLightScript1*)SceneManager::GetInstance()->
                       GetCurrentScene()->GetGameObject("CENTRAL LIGHT 1")->
                       GetComponent("CentralLightScript1");
        script->Activate();
    }
}

/**
    @brief that function fixs the components updates of the forest two.
*/
void ForestActivatorScript2::FixedComponentUpdate() {

}
<|endoftext|>


<commit_before>#include "SceneEditor.hpp"

#include <Engine/Hymn.hpp>
#include <Engine/Util/FileSystem.hpp>
#include <Engine/Util/Log.hpp>
#include <imgui.h>

using namespace GUI;

void SceneEditor::Show() {
    if (ImGui::Begin(("Scene: " + Hymn().scenes[sceneIndex] + "###Scene").c_str(), &visible, ImGuiWindowFlags_NoResize | ImGuiWindowFlags_ShowBorders)) {
        ImGui::InputText("Name", name, 128);
        Hymn().scenes[sceneIndex] = name;

        // Entities.
        entityPressed = false;
        ShowEntity(Hymn().world.GetRoot());
    }
    ImGui::End();
}

void SceneEditor::SetScene(std::size_t sceneIndex) {
    entityEditor.SetVisible(false);

    if (sceneIndex < Hymn().scenes.size()) {
        this->sceneIndex = sceneIndex;
        strcpy(name, Hymn().scenes[sceneIndex].c_str());
    }
}

bool SceneEditor::IsVisible() const {
    return visible;
}

void SceneEditor::SetVisible(bool visible) {
    this->visible = visible;
}

void SceneEditor::Save() const {
    if (sceneIndex < Hymn().scenes.size())
        Hymn().world.Save(Hymn().GetPath() + FileSystem::DELIMITER + "Scenes" + FileSystem::DELIMITER + Hymn().scenes[sceneIndex] + ".json");
}

void SceneEditor::ShowEntity(Entity* entity) {
    bool leaf = entity->IsScene() || entity->GetChildren().empty();
    bool opened = ImGui::TreeNodeEx(entity->name.c_str(), leaf ? ImGuiTreeNodeFlags_Leaf : 0);
    bool instantiate = false;

    if (ImGui::BeginPopupContextItem(entity->name.c_str())) {
        if (ImGui::Selectable("Edit")) {
            entityPressed = true;
            entityEditor.SetEntity(entity);
        }

        if (!entity->IsScene()) {
            if (ImGui::Selectable("Add child"))
                entity->AddChild("Entity #" + std::to_string(Hymn().entityNumber++));

            if (ImGui::Selectable("Instantiate scene"))
                instantiate = true;
        }

        if (entity != Hymn().world.GetRoot()) {
            if (ImGui::Selectable("Delete")) {
                entity->Kill();
                if (entityEditor.ShowsEntity(entity))
                    entityEditor.SetVisible(false);
            }
        }

        ImGui::EndPopup();
    }

    if (instantiate)
        ImGui::OpenPopup("Select scene");

    if (ImGui::BeginPopup("Select scene")) {
        ImGui::Text("Scenes");
        ImGui::Separator();

        for (const std::string& scene : Hymn().scenes) {
            if (ImGui::Selectable(scene.c_str()))
                entity->InstantiateScene(scene);
        }

        ImGui::EndPopup();
    }

    if (opened) {
        if (!entity->IsScene()) {
            for (Entity* child : entity->GetChildren()) {
                ShowEntity(child);
            }
        }

        ImGui::TreePop();
    }
}
<commit_msg>Fix wrong scene being overwritten<commit_after>#include "SceneEditor.hpp"

#include <Engine/Hymn.hpp>
#include <Engine/Util/FileSystem.hpp>
#include <Engine/Util/Log.hpp>
#include <imgui.h>

using namespace GUI;

void SceneEditor::Show() {
    if (ImGui::Begin(("Scene: " + Hymn().scenes[sceneIndex] + "###Scene").c_str(), &visible, ImGuiWindowFlags_NoResize | ImGuiWindowFlags_ShowBorders)) {
        ImGui::InputText("Name", name, 128);
        Hymn().scenes[sceneIndex] = name;

        // Entities.
        entityPressed = false;
        ShowEntity(Hymn().world.GetRoot());
    }
    ImGui::End();
}

void SceneEditor::SetScene(std::size_t sceneIndex) {
    entityEditor.SetVisible(false);
    this->sceneIndex = sceneIndex;

    if (sceneIndex < Hymn().scenes.size()) {
        strcpy(name, Hymn().scenes[sceneIndex].c_str());
    } else {
        SetVisible(false);
    }
}

bool SceneEditor::IsVisible() const {
    return visible;
}

void SceneEditor::SetVisible(bool visible) {
    this->visible = visible;
}

void SceneEditor::Save() const {
    if (sceneIndex < Hymn().scenes.size())
        Hymn().world.Save(Hymn().GetPath() + FileSystem::DELIMITER + "Scenes" + FileSystem::DELIMITER + Hymn().scenes[sceneIndex] + ".json");
}

void SceneEditor::ShowEntity(Entity* entity) {
    bool leaf = entity->IsScene() || entity->GetChildren().empty();
    bool opened = ImGui::TreeNodeEx(entity->name.c_str(), leaf ? ImGuiTreeNodeFlags_Leaf : 0);
    bool instantiate = false;

    if (ImGui::BeginPopupContextItem(entity->name.c_str())) {
        if (ImGui::Selectable("Edit")) {
            entityPressed = true;
            entityEditor.SetEntity(entity);
        }

        if (!entity->IsScene()) {
            if (ImGui::Selectable("Add child"))
                entity->AddChild("Entity #" + std::to_string(Hymn().entityNumber++));

            if (ImGui::Selectable("Instantiate scene"))
                instantiate = true;
        }

        if (entity != Hymn().world.GetRoot()) {
            if (ImGui::Selectable("Delete")) {
                entity->Kill();
                if (entityEditor.ShowsEntity(entity))
                    entityEditor.SetVisible(false);
            }
        }

        ImGui::EndPopup();
    }

    if (instantiate)
        ImGui::OpenPopup("Select scene");

    if (ImGui::BeginPopup("Select scene")) {
        ImGui::Text("Scenes");
        ImGui::Separator();

        for (const std::string& scene : Hymn().scenes) {
            if (ImGui::Selectable(scene.c_str()))
                entity->InstantiateScene(scene);
        }

        ImGui::EndPopup();
    }

    if (opened) {
        if (!entity->IsScene()) {
            for (Entity* child : entity->GetChildren()) {
                ShowEntity(child);
            }
        }

        ImGui::TreePop();
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2006-2008 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 "base/file_util.h"
#include "base/path_service.h"
#include "base/string_util.h"
#include "chrome/app/chrome_dll_resource.h"
#include "chrome/browser/automation/url_request_mock_http_job.h"
#include "chrome/browser/download/save_package.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/test/automation/browser_proxy.h"
#include "chrome/test/automation/tab_proxy.h"
#include "chrome/test/ui/ui_test.h"
#include "net/url_request/url_request_unittest.h"

const std::wstring kTestDir = L"save_page";

class SavePageTest : public UITest {
 protected:
  SavePageTest() : UITest() {}

  void CheckFile(const std::wstring& client_file,
                 const std::wstring& server_file,
                 bool check_equal) {
    bool exist = false;
    for (int i = 0; i < 20; ++i) {
      if (file_util::PathExists(client_file)) {
        exist = true;
        break;
      }
      Sleep(sleep_timeout_ms());
    }
    EXPECT_TRUE(exist);

    if (check_equal) {
      std::wstring server_file_name;
      ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA,
                                   &server_file_name));
      server_file_name += L"\\" + kTestDir + L"\\" + server_file;
      ASSERT_TRUE(file_util::PathExists(server_file_name));

      int64 client_file_size = 0;
      int64 server_file_size = 0;
      EXPECT_TRUE(file_util::GetFileSize(client_file, &client_file_size));
      EXPECT_TRUE(file_util::GetFileSize(server_file_name, &server_file_size));
      EXPECT_EQ(client_file_size, server_file_size);
      EXPECT_TRUE(file_util::ContentsEqual(client_file, server_file_name));
    }

    EXPECT_TRUE(DieFileDie(client_file, false));
  }

  virtual void SetUp() {
    UITest::SetUp();
    EXPECT_TRUE(file_util::CreateNewTempDirectory(L"", &save_dir_));
    save_dir_ += FilePath::kSeparators[0];

    download_dir_ = GetDownloadDirectory();
    download_dir_ += FilePath::kSeparators[0];
  }

  virtual void TearDown() {
    UITest::TearDown();
    DieFileDie(save_dir_, true);
  }

  std::wstring save_dir_;
  std::wstring download_dir_;
};

TEST_F(SavePageTest, SaveHTMLOnly) {
  std::wstring file_name = L"a.htm";
  std::wstring full_file_name = save_dir_ + file_name;
  std::wstring dir = save_dir_ + L"a_files";

  GURL url = URLRequestMockHTTPJob::GetMockUrl(kTestDir + L"/" + file_name);
  scoped_ptr<TabProxy> tab(GetActiveTab());
  ASSERT_TRUE(tab->NavigateToURL(url));
  WaitUntilTabCount(1);

  EXPECT_TRUE(tab->SavePage(full_file_name, dir,
      SavePackage::SAVE_AS_ONLY_HTML));
  EXPECT_TRUE(WaitForDownloadShelfVisible(tab.get()));

  CheckFile(full_file_name, file_name, true);
  EXPECT_FALSE(file_util::PathExists(dir));
}

TEST_F(SavePageTest, SaveCompleteHTML) {
  std::wstring file_name = L"b.htm";
  std::wstring full_file_name = save_dir_ + file_name;
  std::wstring dir = save_dir_ + L"b_files";

  GURL url = URLRequestMockHTTPJob::GetMockUrl(kTestDir + L"/" + file_name);
  scoped_ptr<TabProxy> tab(GetActiveTab());
  ASSERT_TRUE(tab->NavigateToURL(url));
  WaitUntilTabCount(1);

  EXPECT_TRUE(tab->SavePage(full_file_name, dir,
      SavePackage::SAVE_AS_COMPLETE_HTML));
  EXPECT_TRUE(WaitForDownloadShelfVisible(tab.get()));

  CheckFile(dir + L"\\1.png", L"1.png", true);
  CheckFile(dir + L"\\1.css", L"1.css", true);
  CheckFile(full_file_name, file_name, false);
  EXPECT_TRUE(DieFileDie(dir, true));
}

TEST_F(SavePageTest, NoSave) {
  std::wstring file_name = L"c.htm";
  std::wstring full_file_name = save_dir_ + file_name;
  std::wstring dir = save_dir_ + L"c_files";

  scoped_ptr<TabProxy> tab(GetActiveTab());
  ASSERT_TRUE(tab->NavigateToURL(GURL(L"about:blank")));
  WaitUntilTabCount(1);

  EXPECT_FALSE(tab->SavePage(full_file_name, dir,
      SavePackage::SAVE_AS_ONLY_HTML));
  EXPECT_FALSE(WaitForDownloadShelfVisible(tab.get()));
}

TEST_F(SavePageTest, FilenameFromPageTitle) {
  std::wstring file_name = L"b.htm";
  std::wstring full_file_name = download_dir_ +
      L"Test page for saving page feature.htm";
  std::wstring dir = download_dir_ +
      L"Test page for saving page feature_files";

  GURL url = URLRequestMockHTTPJob::GetMockUrl(kTestDir + L"/" + file_name);
  scoped_ptr<TabProxy> tab(GetActiveTab());
  ASSERT_TRUE(tab->NavigateToURL(url));
  WaitUntilTabCount(1);

  scoped_ptr<BrowserProxy> browser(automation()->GetBrowserWindow(0));
  automation()->SavePackageShouldPromptUser(false);
  EXPECT_TRUE(browser->RunCommand(IDC_SAVE_PAGE));
  EXPECT_TRUE(WaitForDownloadShelfVisible(tab.get()));
  automation()->SavePackageShouldPromptUser(true);

  CheckFile(dir + L"\\1.png", L"1.png", true);
  CheckFile(dir + L"\\1.css", L"1.css", true);
  CheckFile(full_file_name, file_name, false);
  EXPECT_TRUE(DieFileDie(full_file_name, false));
  EXPECT_TRUE(DieFileDie(dir, true));
}

TEST_F(SavePageTest, CleanFilenameFromPageTitle) {
  std::wstring file_name = L"c.htm";
  std::wstring full_file_name = download_dir_ + L"test.htm";
  std::wstring dir = download_dir_ + L"test_files";

  GURL url = URLRequestMockHTTPJob::GetMockUrl(kTestDir + L"/" + file_name);
  scoped_ptr<TabProxy> tab(GetActiveTab());
  ASSERT_TRUE(tab->NavigateToURL(url));
  WaitUntilTabCount(1);

  scoped_ptr<BrowserProxy> browser(automation()->GetBrowserWindow(0));
  automation()->SavePackageShouldPromptUser(false);
  EXPECT_TRUE(browser->RunCommand(IDC_SAVE_PAGE));
  EXPECT_TRUE(WaitForDownloadShelfVisible(tab.get()));
  automation()->SavePackageShouldPromptUser(true);

  CheckFile(full_file_name, file_name, false);
  EXPECT_TRUE(DieFileDie(full_file_name, false));
  EXPECT_TRUE(DieFileDie(dir, true));
}
<commit_msg>Disable the test SavePageTest.CleanFilenameFromPageTitle due to bug 6514. Review URL: http://codereview.chromium.org/18154<commit_after>// Copyright (c) 2006-2008 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 "base/file_util.h"
#include "base/path_service.h"
#include "base/string_util.h"
#include "chrome/app/chrome_dll_resource.h"
#include "chrome/browser/automation/url_request_mock_http_job.h"
#include "chrome/browser/download/save_package.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/test/automation/browser_proxy.h"
#include "chrome/test/automation/tab_proxy.h"
#include "chrome/test/ui/ui_test.h"
#include "net/url_request/url_request_unittest.h"

const std::wstring kTestDir = L"save_page";

class SavePageTest : public UITest {
 protected:
  SavePageTest() : UITest() {}

  void CheckFile(const std::wstring& client_file,
                 const std::wstring& server_file,
                 bool check_equal) {
    bool exist = false;
    for (int i = 0; i < 20; ++i) {
      if (file_util::PathExists(client_file)) {
        exist = true;
        break;
      }
      Sleep(sleep_timeout_ms());
    }
    EXPECT_TRUE(exist);

    if (check_equal) {
      std::wstring server_file_name;
      ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA,
                                   &server_file_name));
      server_file_name += L"\\" + kTestDir + L"\\" + server_file;
      ASSERT_TRUE(file_util::PathExists(server_file_name));

      int64 client_file_size = 0;
      int64 server_file_size = 0;
      EXPECT_TRUE(file_util::GetFileSize(client_file, &client_file_size));
      EXPECT_TRUE(file_util::GetFileSize(server_file_name, &server_file_size));
      EXPECT_EQ(client_file_size, server_file_size);
      EXPECT_TRUE(file_util::ContentsEqual(client_file, server_file_name));
    }

    EXPECT_TRUE(DieFileDie(client_file, false));
  }
SavePageTest
  virtual void SetUp() {
    UITest::SetUp();
    EXPECT_TRUE(file_util::CreateNewTempDirectory(L"", &save_dir_));
    save_dir_ += FilePath::kSeparators[0];

    download_dir_ = GetDownloadDirectory();
    download_dir_ += FilePath::kSeparators[0];
  }

  virtual void TearDown() {
    UITest::TearDown();
    DieFileDie(save_dir_, true);
  }

  std::wstring save_dir_;
  std::wstring download_dir_;
};

TEST_F(SavePageTest, SaveHTMLOnly) {
  std::wstring file_name = L"a.htm";
  std::wstring full_file_name = save_dir_ + file_name;
  std::wstring dir = save_dir_ + L"a_files";

  GURL url = URLRequestMockHTTPJob::GetMockUrl(kTestDir + L"/" + file_name);
  scoped_ptr<TabProxy> tab(GetActiveTab());
  ASSERT_TRUE(tab->NavigateToURL(url));
  WaitUntilTabCount(1);

  EXPECT_TRUE(tab->SavePage(full_file_name, dir,
      SavePackage::SAVE_AS_ONLY_HTML));
  EXPECT_TRUE(WaitForDownloadShelfVisible(tab.get()));

  CheckFile(full_file_name, file_name, true);
  EXPECT_FALSE(file_util::PathExists(dir));
}

TEST_F(SavePageTest, SaveCompleteHTML) {
  std::wstring file_name = L"b.htm";
  std::wstring full_file_name = save_dir_ + file_name;
  std::wstring dir = save_dir_ + L"b_files";

  GURL url = URLRequestMockHTTPJob::GetMockUrl(kTestDir + L"/" + file_name);
  scoped_ptr<TabProxy> tab(GetActiveTab());
  ASSERT_TRUE(tab->NavigateToURL(url));
  WaitUntilTabCount(1);

  EXPECT_TRUE(tab->SavePage(full_file_name, dir,
      SavePackage::SAVE_AS_COMPLETE_HTML));
  EXPECT_TRUE(WaitForDownloadShelfVisible(tab.get()));

  CheckFile(dir + L"\\1.png", L"1.png", true);
  CheckFile(dir + L"\\1.css", L"1.css", true);
  CheckFile(full_file_name, file_name, false);
  EXPECT_TRUE(DieFileDie(dir, true));
}

TEST_F(SavePageTest, NoSave) {
  std::wstring file_name = L"c.htm";
  std::wstring full_file_name = save_dir_ + file_name;
  std::wstring dir = save_dir_ + L"c_files";

  scoped_ptr<TabProxy> tab(GetActiveTab());
  ASSERT_TRUE(tab->NavigateToURL(GURL(L"about:blank")));
  WaitUntilTabCount(1);

  EXPECT_FALSE(tab->SavePage(full_file_name, dir,
      SavePackage::SAVE_AS_ONLY_HTML));
  EXPECT_FALSE(WaitForDownloadShelfVisible(tab.get()));
}

TEST_F(SavePageTest, FilenameFromPageTitle) {
  std::wstring file_name = L"b.htm";
  std::wstring full_file_name = download_dir_ +
      L"Test page for saving page feature.htm";
  std::wstring dir = download_dir_ +
      L"Test page for saving page feature_files";

  GURL url = URLRequestMockHTTPJob::GetMockUrl(kTestDir + L"/" + file_name);
  scoped_ptr<TabProxy> tab(GetActiveTab());
  ASSERT_TRUE(tab->NavigateToURL(url));
  WaitUntilTabCount(1);

  scoped_ptr<BrowserProxy> browser(automation()->GetBrowserWindow(0));
  automation()->SavePackageShouldPromptUser(false);
  EXPECT_TRUE(browser->RunCommand(IDC_SAVE_PAGE));
  EXPECT_TRUE(WaitForDownloadShelfVisible(tab.get()));
  automation()->SavePackageShouldPromptUser(true);

  CheckFile(dir + L"\\1.png", L"1.png", true);
  CheckFile(dir + L"\\1.css", L"1.css", true);
  CheckFile(full_file_name, file_name, false);
  EXPECT_TRUE(DieFileDie(full_file_name, false));
  EXPECT_TRUE(DieFileDie(dir, true));
}

// BUG 6514
TEST_F(SavePageTest, DISABLED_CleanFilenameFromPageTitle) {
  std::wstring file_name = L"c.htm";
  std::wstring full_file_name = download_dir_ + L"test.htm";
  std::wstring dir = download_dir_ + L"test_files";

  GURL url = URLRequestMockHTTPJob::GetMockUrl(kTestDir + L"/" + file_name);
  scoped_ptr<TabProxy> tab(GetActiveTab());
  ASSERT_TRUE(tab->NavigateToURL(url));
  WaitUntilTabCount(1);

  scoped_ptr<BrowserProxy> browser(automation()->GetBrowserWindow(0));
  automation()->SavePackageShouldPromptUser(false);
  EXPECT_TRUE(browser->RunCommand(IDC_SAVE_PAGE));
  EXPECT_TRUE(WaitForDownloadShelfVisible(tab.get()));
  automation()->SavePackageShouldPromptUser(true);

  CheckFile(full_file_name, file_name, false);
  EXPECT_TRUE(DieFileDie(full_file_name, false));
  EXPECT_TRUE(DieFileDie(dir, true));
}
<|endoftext|>


<commit_before>// -*- Mode: C++; tab-width: 2; -*-
// vi: set ts=2:
//
// $Id: ConjugateGradientMinimizer_test.C,v 1.10 2003/03/14 10:11:28 oliver Exp $

#include <BALL/CONCEPT/classTest.h>

///////////////////////////
#include <BALL/MOLMEC/MINIMIZATION/conjugateGradient.h>
#include <BALL/MOLMEC/AMBER/amber.h>
#include <BALL/DATATYPE/options.h>
#include <BALL/KERNEL/PTE.h>
///////////////////////////

START_TEST(ConjugateGradienMinimizer, "$Id: ConjugateGradientMinimizer_test.C,v 1.10 2003/03/14 10:11:28 oliver Exp $")

using namespace BALL;

/////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////

System S;
AmberFF FF(S);

ConjugateGradientMinimizer*	em;
CHECK(ConjugateGradientMinimizer::ConjugateGradientMinimizer())
	em = new ConjugateGradientMinimizer();
	TEST_NOT_EQUAL(em, 0)
RESULT

CHECK(ConjugateGradientMinimizer::~ConjugateGradientMinimizer())
	delete em;
RESULT

CHECK(ConjugateGradientMinimizer::getForceField())
	ConjugateGradientMinimizer em;
	TEST_EQUAL(em.getForceField(), 0)
RESULT

CHECK(ConjugateGradientMinimizer::ConjugateGradientMinimizer(ForceField&))
	TEST_EQUAL(FF.isValid(), true)
	ConjugateGradientMinimizer em(FF);
	TEST_EQUAL(em.getForceField(), &FF)
RESULT

CHECK(ConjugateGradientMinimizer::ConjugateGradientMinimizer(ForceField&, const Options&))
	Options options;
	options.set("ABC", "DEF");
	em = new ConjugateGradientMinimizer(FF, options);
	TEST_EQUAL(em->getForceField(), &FF)
	TEST_EQUAL(em->options["ABC"], "DEF")
	delete em;
RESULT

CHECK(ConjugateGradientMinimizer::ConjugateGradientMinimizer(const ConjugateGradientMinimizer&, bool))
	ConjugateGradientMinimizer em1;
	ConjugateGradientMinimizer em2(em1);
	bool test = (em1 == em2);
	TEST_EQUAL(test, true)
	em1.setup(FF);
	ConjugateGradientMinimizer em3(em1);
	test = (em1 == em3);
	TEST_EQUAL(test, true)
	em1.setup(FF);
RESULT

CHECK(ConjugateGradientMinimizer::operator = (const ConjugateGradientMinimizer&))
	ConjugateGradientMinimizer em1;
	ConjugateGradientMinimizer em2 = em1;
	bool test = (em1 == em2);
	TEST_EQUAL(test, true)
	em1.setup(FF);
	ConjugateGradientMinimizer em3 = em1;
	test = (em1 == em3);
	TEST_EQUAL(test, true)
RESULT

CHECK(ConjugateGradientMinimizer::isValid() const)
	em = new ConjugateGradientMinimizer;
	TEST_EQUAL(em->isValid(), false)
	delete em;
	em = new ConjugateGradientMinimizer(FF);
	TEST_EQUAL(em->isValid(), true)
	delete em;
RESULT

CHECK(ConjugateGradientMinimizer::setup(ForceField&))
	ConjugateGradientMinimizer e_min(FF);
	TEST_EQUAL(e_min.getForceUpdateCounter(), 0)
	TEST_EQUAL(e_min.getEnergyUpdateCounter(), 0)
	bool test = (e_min.getForceField() == &FF);
	TEST_EQUAL(test, true)
RESULT

CHECK(ConjugateGradientMinimizer::setup(ForceField&, const Options&))
	Options options;
	options.setInteger(EnergyMinimizer::Option::ENERGY_OUTPUT_FREQUENCY, 3456);
	ConjugateGradientMinimizer e_min(FF, options);
	TEST_EQUAL(e_min.getForceUpdateCounter(), 0)
	TEST_EQUAL(e_min.getEnergyUpdateCounter(), 0)
	bool test = (e_min.getForceField() == &FF);
	TEST_EQUAL(test, true)
	TEST_EQUAL(e_min.getEnergyOutputFrequency(), 3456)
RESULT

CHECK(ConjugateGradientMinimizer::specificSetup())
	ConjugateGradientMinimizer em(FF);
	TEST_EQUAL(em.specificSetup(), true)
	// specificSetup() shouldn't do anything except returning true
RESULT

CHECK(ConjugateGradientMinimizer::getNumberOfIterations() const)
	ConjugateGradientMinimizer e_min;
	TEST_EQUAL(e_min.getNumberOfIterations(), 0)
RESULT

CHECK(ConjugateGradientMinimizer::setNumberOfIteration(Size))
	ConjugateGradientMinimizer e_min;
	e_min.setNumberOfIterations(4);
	TEST_EQUAL(e_min.getNumberOfIterations(), 4)
RESULT

CHECK(ConjugateGradientMinimizer::getMaxNumberOfIterations())
	ConjugateGradientMinimizer e_min;
	TEST_EQUAL(e_min.getMaxNumberOfIterations(), 0)
RESULT

CHECK(ConjugateGradientMinimizer::setMaxNumberOfIterations(Size))
	ConjugateGradientMinimizer e_min;
	e_min.setMaxNumberOfIterations(2000);
	TEST_EQUAL(e_min.getMaxNumberOfIterations(), 2000)
RESULT

CHECK(ConjugateGradientMinimizer::getEnergyOutputFrequency() const)
	ConjugateGradientMinimizer e_min2;
	TEST_EQUAL(e_min2.getEnergyOutputFrequency(), 0)
RESULT

CHECK(ConjugateGradientMinimizer::setEnergyOutputFrequency(Size))
	ConjugateGradientMinimizer e_min;
	e_min.setEnergyOutputFrequency(8);
	TEST_EQUAL(e_min.getEnergyOutputFrequency(), 8)
RESULT

CHECK(ConjugateGradientMinimizer::getEnergyDifferenceBound() const)
	ConjugateGradientMinimizer e_min;
	TEST_EQUAL(e_min.getEnergyDifferenceBound(), 0.0)
RESULT

CHECK(ConjugateGradientMinimizer::setEnergyDifferenceBound(float))
	ConjugateGradientMinimizer e_min;
	e_min.setEnergyDifferenceBound(9.0);
	TEST_EQUAL(e_min.getEnergyDifferenceBound(), 9.0)
RESULT

CHECK(ConjugateGradientMinimizer::getMaximumDisplacement() const)
	ConjugateGradientMinimizer e_min;
	TEST_EQUAL(e_min.getMaximumDisplacement(), 0.0)
RESULT

CHECK(ConjugateGradientMinimizer::setMaximumDisplacement(float))
	ConjugateGradientMinimizer e_min;
	e_min.setMaximumDisplacement(56.0);
	TEST_EQUAL(e_min.getMaximumDisplacement(), 56.0)
RESULT

CHECK(ConjugateGradientMinimizer::minimize(Size, bool))
	System S;
	Molecule* m = new Molecule;
	Atom* a1 = new Atom;
	Atom* a2 = new Atom;
	S.insert(*m);
	m->insert(*a1);
	m->insert(*a2);
	a1->setPosition(Vector3(-0.5, 0, 0));
	a2->setPosition(Vector3(0.5, 0, 0));
	a1->setElement(PTE[Element::C]);
	a2->setElement(PTE[Element::C]);
	a1->setTypeName("C");
	a2->setTypeName("C");
	a1->setCharge(0.0);
	a2->setCharge(0.0);
	FF.options[AmberFF::Option::ASSIGN_CHARGES] = "false";
	FF.setup(S);

	TEST_EQUAL(FF.isValid(), true)
	FF.updateEnergy();
	FF.updateForces();
	PRECISION(10)
	TEST_REAL_EQUAL(FF.getEnergy(), 3.42854e6)

	PRECISION(1e-4)
	ConjugateGradientMinimizer cgm(FF);
	cgm.setEnergyOutputFrequency(1);
	cgm.setMaxGradient(0.0005);
	cgm.setEnergyDifferenceBound(0.00000001);
	TEST_EQUAL(cgm.isValid(), true)
	FF.updateEnergy();
	FF.updateForces();
	bool result = cgm.minimize(20);
	TEST_EQUAL(result, true)
	FF.updateEnergy();
	FF.updateForces();

	TEST_REAL_EQUAL(FF.getEnergy(), -0.359813)
	TEST_EQUAL(FF.getRMSGradient() <= cgm.getMaxGradient(), true)
	PRECISION(5e-3)
	TEST_REAL_EQUAL(a1->getPosition().getDistance(a2->getPosition()), 3.81244)
RESULT

/////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////

END_TEST
<commit_msg>Added more explicit tests for the minimizer<commit_after>// -*- Mode: C++; tab-width: 2; -*-
// vi: set ts=2:
//
// $Id: ConjugateGradientMinimizer_test.C,v 1.11 2003/03/21 17:33:05 anhi Exp $

#include <BALL/CONCEPT/classTest.h>

///////////////////////////
#include <BALL/MOLMEC/MINIMIZATION/conjugateGradient.h>
#include <BALL/MOLMEC/AMBER/amber.h>
#include <BALL/DATATYPE/options.h>
#include <BALL/KERNEL/PTE.h>
#include <BALL/FORMAT/HINFile.h>
#include <BALL/MATHS/analyticalGeometry.h>
///////////////////////////

START_TEST(ConjugateGradienMinimizer, "$Id: ConjugateGradientMinimizer_test.C,v 1.11 2003/03/21 17:33:05 anhi Exp $")

using namespace BALL;

/////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////

System S;
AmberFF FF(S);

ConjugateGradientMinimizer*	em;
CHECK(ConjugateGradientMinimizer::ConjugateGradientMinimizer())
	em = new ConjugateGradientMinimizer();
	TEST_NOT_EQUAL(em, 0)
RESULT

CHECK(ConjugateGradientMinimizer::~ConjugateGradientMinimizer())
	delete em;
RESULT

CHECK(ConjugateGradientMinimizer::getForceField())
	ConjugateGradientMinimizer em;
	TEST_EQUAL(em.getForceField(), 0)
RESULT

CHECK(ConjugateGradientMinimizer::ConjugateGradientMinimizer(ForceField&))
	TEST_EQUAL(FF.isValid(), true)
	ConjugateGradientMinimizer em(FF);
	TEST_EQUAL(em.getForceField(), &FF)
RESULT

CHECK(ConjugateGradientMinimizer::ConjugateGradientMinimizer(ForceField&, const Options&))
	Options options;
	options.set("ABC", "DEF");
	em = new ConjugateGradientMinimizer(FF, options);
	TEST_EQUAL(em->getForceField(), &FF)
	TEST_EQUAL(em->options["ABC"], "DEF")
	delete em;
RESULT

CHECK(ConjugateGradientMinimizer::ConjugateGradientMinimizer(const ConjugateGradientMinimizer&, bool))
	ConjugateGradientMinimizer em1;
	ConjugateGradientMinimizer em2(em1);
	bool test = (em1 == em2);
	TEST_EQUAL(test, true)
	em1.setup(FF);
	ConjugateGradientMinimizer em3(em1);
	test = (em1 == em3);
	TEST_EQUAL(test, true)
	em1.setup(FF);
RESULT

CHECK(ConjugateGradientMinimizer::operator = (const ConjugateGradientMinimizer&))
	ConjugateGradientMinimizer em1;
	ConjugateGradientMinimizer em2 = em1;
	bool test = (em1 == em2);
	TEST_EQUAL(test, true)
	em1.setup(FF);
	ConjugateGradientMinimizer em3 = em1;
	test = (em1 == em3);
	TEST_EQUAL(test, true)
RESULT

CHECK(ConjugateGradientMinimizer::isValid() const)
	em = new ConjugateGradientMinimizer;
	TEST_EQUAL(em->isValid(), false)
	delete em;
	em = new ConjugateGradientMinimizer(FF);
	TEST_EQUAL(em->isValid(), true)
	delete em;
RESULT

CHECK(ConjugateGradientMinimizer::setup(ForceField&))
	ConjugateGradientMinimizer e_min(FF);
	TEST_EQUAL(e_min.getForceUpdateCounter(), 0)
	TEST_EQUAL(e_min.getEnergyUpdateCounter(), 0)
	bool test = (e_min.getForceField() == &FF);
	TEST_EQUAL(test, true)
RESULT

CHECK(ConjugateGradientMinimizer::setup(ForceField&, const Options&))
	Options options;
	options.setInteger(EnergyMinimizer::Option::ENERGY_OUTPUT_FREQUENCY, 3456);
	ConjugateGradientMinimizer e_min(FF, options);
	TEST_EQUAL(e_min.getForceUpdateCounter(), 0)
	TEST_EQUAL(e_min.getEnergyUpdateCounter(), 0)
	bool test = (e_min.getForceField() == &FF);
	TEST_EQUAL(test, true)
	TEST_EQUAL(e_min.getEnergyOutputFrequency(), 3456)
RESULT

CHECK(ConjugateGradientMinimizer::specificSetup())
	ConjugateGradientMinimizer em(FF);
	TEST_EQUAL(em.specificSetup(), true)
	// specificSetup() shouldn't do anything except returning true
RESULT

CHECK(ConjugateGradientMinimizer::getNumberOfIterations() const)
	ConjugateGradientMinimizer e_min;
	TEST_EQUAL(e_min.getNumberOfIterations(), 0)
RESULT

CHECK(ConjugateGradientMinimizer::setNumberOfIteration(Size))
	ConjugateGradientMinimizer e_min;
	e_min.setNumberOfIterations(4);
	TEST_EQUAL(e_min.getNumberOfIterations(), 4)
RESULT

CHECK(ConjugateGradientMinimizer::getMaxNumberOfIterations())
	ConjugateGradientMinimizer e_min;
	TEST_EQUAL(e_min.getMaxNumberOfIterations(), 0)
RESULT

CHECK(ConjugateGradientMinimizer::setMaxNumberOfIterations(Size))
	ConjugateGradientMinimizer e_min;
	e_min.setMaxNumberOfIterations(2000);
	TEST_EQUAL(e_min.getMaxNumberOfIterations(), 2000)
RESULT

CHECK(ConjugateGradientMinimizer::getEnergyOutputFrequency() const)
	ConjugateGradientMinimizer e_min2;
	TEST_EQUAL(e_min2.getEnergyOutputFrequency(), 0)
RESULT

CHECK(ConjugateGradientMinimizer::setEnergyOutputFrequency(Size))
	ConjugateGradientMinimizer e_min;
	e_min.setEnergyOutputFrequency(8);
	TEST_EQUAL(e_min.getEnergyOutputFrequency(), 8)
RESULT

CHECK(ConjugateGradientMinimizer::getEnergyDifferenceBound() const)
	ConjugateGradientMinimizer e_min;
	TEST_EQUAL(e_min.getEnergyDifferenceBound(), 0.0)
RESULT

CHECK(ConjugateGradientMinimizer::setEnergyDifferenceBound(float))
	ConjugateGradientMinimizer e_min;
	e_min.setEnergyDifferenceBound(9.0);
	TEST_EQUAL(e_min.getEnergyDifferenceBound(), 9.0)
RESULT

CHECK(ConjugateGradientMinimizer::getMaximumDisplacement() const)
	ConjugateGradientMinimizer e_min;
	TEST_EQUAL(e_min.getMaximumDisplacement(), 0.0)
RESULT

CHECK(ConjugateGradientMinimizer::setMaximumDisplacement(float))
	ConjugateGradientMinimizer e_min;
	e_min.setMaximumDisplacement(56.0);
	TEST_EQUAL(e_min.getMaximumDisplacement(), 56.0)
RESULT

CHECK(ConjugateGradientMinimizer::minimize(Size, bool))
	System S;
	Molecule* m = new Molecule;
	Atom* a1 = new Atom;
	Atom* a2 = new Atom;
	S.insert(*m);
	m->insert(*a1);
	m->insert(*a2);
	a1->setPosition(Vector3(-0.5, 0, 0));
	a2->setPosition(Vector3(0.5, 0, 0));
	a1->setElement(PTE[Element::C]);
	a2->setElement(PTE[Element::C]);
	a1->setTypeName("C");
	a2->setTypeName("C");
	a1->setCharge(0.0);
	a2->setCharge(0.0);
	FF.options[AmberFF::Option::ASSIGN_CHARGES] = "false";
	FF.setup(S);

	TEST_EQUAL(FF.isValid(), true)
	FF.updateEnergy();
	FF.updateForces();
	PRECISION(10)
	TEST_REAL_EQUAL(FF.getEnergy(), 3.42854e6)

	PRECISION(1e-4)
	ConjugateGradientMinimizer cgm(FF);
	cgm.setEnergyOutputFrequency(1);
	cgm.setMaxGradient(0.0005);
	cgm.setEnergyDifferenceBound(0.00000001);
	TEST_EQUAL(cgm.isValid(), true)
	FF.updateEnergy();
	FF.updateForces();
	bool result = cgm.minimize(20);
	TEST_EQUAL(result, true)
	FF.updateEnergy();
	FF.updateForces();

	TEST_REAL_EQUAL(FF.getEnergy(), -0.359813)
	TEST_EQUAL(FF.getRMSGradient() <= cgm.getMaxGradient(), true)
	PRECISION(5e-3)
	TEST_REAL_EQUAL(a1->getPosition().getDistance(a2->getPosition()), 3.81244)
RESULT

CHECK(ConjugateGradientMinimizer::minimize(Size, bool, SHANNO))
	System S;
	HINFile f("data/ethan.hin");
	f >> S;
	FF.options[AmberFF::Option::ASSIGN_CHARGES] = "false";
	FF.setup(S);

	TEST_EQUAL(FF.isValid(), true)
	FF.updateEnergy();
	FF.updateForces();
	PRECISION(1E-4)
	TEST_REAL_EQUAL(FF.getEnergy(), 18.5605)

	ConjugateGradientMinimizer cgm(FF);

	cgm.setEnergyOutputFrequency(5);
	cgm.setMaxGradient(0.01);
	cgm.setEnergyDifferenceBound(0.00000001);
	bool result = cgm.setUpdateMethod("SHANNO");
	TEST_EQUAL(result, true)
	ConjugateGradientMinimizer::UpdateMethod um;
	um = cgm.getUpdateMethod();
	TEST_EQUAL(um, ConjugateGradientMinimizer::SHANNO);

	TEST_EQUAL(cgm.isValid(), true)
	FF.updateEnergy();
	FF.updateForces();
	result = cgm.minimize(55);

	TEST_EQUAL(result, true)
	float energy = FF.updateEnergy();
	FF.updateForces();

	PRECISION(1E-3)
	TEST_REAL_EQUAL(energy, 5.906)

	AtomIterator atit;
	Vector3 pos[8];
	int i=0;

	for (atit = S.beginAtom(); +atit; ++atit)
	{
		pos[i] = atit->getPosition();
		++i;
	}

	PRECISION(1E-3)
	Angle torsion;
	torsion = getTorsionAngle(pos[2].x, pos[2].y, pos[2].z, pos[0].x, pos[0].y, pos[0].z, pos[1].x, pos[1].y, pos[1].z, pos[6].x, pos[6].y, pos[6].z);
	TEST_REAL_EQUAL(torsion.toRadian(), 1.047)
	torsion = getTorsionAngle(pos[3].x, pos[3].y, pos[3].z, pos[0].x, pos[0].y, pos[0].z, pos[1].x, pos[1].y, pos[1].z, pos[5].x, pos[5].y, pos[5].z);
	TEST_REAL_EQUAL(torsion.toRadian(), 1.047)
	torsion = getTorsionAngle(pos[4].x, pos[4].y, pos[4].z, pos[0].x, pos[0].y, pos[0].z, pos[1].x, pos[1].y, pos[1].z, pos[7].x, pos[7].y, pos[7].z);
	TEST_REAL_EQUAL(torsion.toRadian(), 1.047)

	PRECISION(1E-2)
	Angle tet;
	tet = (pos[2] - pos[0]).getAngle(pos[3] - pos[0]);
	TEST_REAL_EQUAL(tet.toRadian(), 1.902)
	tet = (pos[2] - pos[0]).getAngle(pos[4] - pos[0]);
	TEST_REAL_EQUAL(tet.toRadian(), 1.902)
	tet = (pos[4] - pos[0]).getAngle(pos[3] - pos[0]);
	TEST_REAL_EQUAL(tet.toRadian(), 1.902)
	tet = (pos[5] - pos[1]).getAngle(pos[6] - pos[1]);
	TEST_REAL_EQUAL(tet.toRadian(), 1.902)
	tet = (pos[5] - pos[1]).getAngle(pos[7] - pos[1]);
	TEST_REAL_EQUAL(tet.toRadian(), 1.902)
	tet = (pos[7] - pos[1]).getAngle(pos[6] - pos[1]);
	TEST_REAL_EQUAL(tet.toRadian(), 1.902)

RESULT

CHECK(ConjugateGradientMinimizer::minimize(Size, bool, FLETCHER_REEVES))
	System S;
	HINFile f("data/ethan.hin");
	f >> S;
	FF.options[AmberFF::Option::ASSIGN_CHARGES] = "false";
	FF.setup(S);

	TEST_EQUAL(FF.isValid(), true)
	FF.updateEnergy();
	FF.updateForces();
	PRECISION(1E-4)
	TEST_REAL_EQUAL(FF.getEnergy(), 18.5605)

	ConjugateGradientMinimizer cgm(FF);

	cgm.setEnergyOutputFrequency(5);
	cgm.setMaxGradient(0.01);
	cgm.setEnergyDifferenceBound(0.00000001);
	bool result = cgm.setUpdateMethod("FLETCHER_REEVES");
	TEST_EQUAL(result, true)
	ConjugateGradientMinimizer::UpdateMethod um;
	um = cgm.getUpdateMethod();
	TEST_EQUAL(um, ConjugateGradientMinimizer::FLETCHER_REEVES);

	TEST_EQUAL(cgm.isValid(), true)
	FF.updateEnergy();
	FF.updateForces();
	result = cgm.minimize(100);

	TEST_EQUAL(result, true)
	float energy = FF.updateEnergy();
	FF.updateForces();

	PRECISION(1E-3)
	TEST_REAL_EQUAL(energy, 5.906)

	AtomIterator atit;
	Vector3 pos[8];
	int i=0;

	for (atit = S.beginAtom(); +atit; ++atit)
	{
		pos[i] = atit->getPosition();
		++i;
	}

	PRECISION(1E-3)
	Angle torsion;
	torsion = getTorsionAngle(pos[2].x, pos[2].y, pos[2].z, pos[0].x, pos[0].y, pos[0].z, pos[1].x, pos[1].y, pos[1].z, pos[6].x, pos[6].y, pos[6].z);
	TEST_REAL_EQUAL(torsion.toRadian(), 1.047)
	torsion = getTorsionAngle(pos[3].x, pos[3].y, pos[3].z, pos[0].x, pos[0].y, pos[0].z, pos[1].x, pos[1].y, pos[1].z, pos[5].x, pos[5].y, pos[5].z);
	TEST_REAL_EQUAL(torsion.toRadian(), 1.047)
	torsion = getTorsionAngle(pos[4].x, pos[4].y, pos[4].z, pos[0].x, pos[0].y, pos[0].z, pos[1].x, pos[1].y, pos[1].z, pos[7].x, pos[7].y, pos[7].z);
	TEST_REAL_EQUAL(torsion.toRadian(), 1.047)

	PRECISION(1E-2)
	Angle tet;
	tet = (pos[2] - pos[0]).getAngle(pos[3] - pos[0]);
	TEST_REAL_EQUAL(tet.toRadian(), 1.902)
	tet = (pos[2] - pos[0]).getAngle(pos[4] - pos[0]);
	TEST_REAL_EQUAL(tet.toRadian(), 1.902)
	tet = (pos[4] - pos[0]).getAngle(pos[3] - pos[0]);
	TEST_REAL_EQUAL(tet.toRadian(), 1.902)
	tet = (pos[5] - pos[1]).getAngle(pos[6] - pos[1]);
	TEST_REAL_EQUAL(tet.toRadian(), 1.902)
	tet = (pos[5] - pos[1]).getAngle(pos[7] - pos[1]);
	TEST_REAL_EQUAL(tet.toRadian(), 1.902)
	tet = (pos[7] - pos[1]).getAngle(pos[6] - pos[1]);
	TEST_REAL_EQUAL(tet.toRadian(), 1.902)

RESULT

CHECK(ConjugateGradientMinimizer::minimize(Size, bool, POLAK_RIBIERE))
	System S;
	HINFile f("data/ethan.hin");
	f >> S;
	FF.options[AmberFF::Option::ASSIGN_CHARGES] = "false";
	FF.setup(S);

	TEST_EQUAL(FF.isValid(), true)
	FF.updateEnergy();
	FF.updateForces();
	PRECISION(1E-4)
	TEST_REAL_EQUAL(FF.getEnergy(), 18.5605)

	ConjugateGradientMinimizer cgm(FF);

	cgm.setEnergyOutputFrequency(5);
	cgm.setMaxGradient(0.01);
	cgm.setEnergyDifferenceBound(0.00000001);
	bool result = cgm.setUpdateMethod("POLAK_RIBIERE");
	TEST_EQUAL(result, true)
	ConjugateGradientMinimizer::UpdateMethod um;
	um = cgm.getUpdateMethod();
	TEST_EQUAL(um, ConjugateGradientMinimizer::POLAK_RIBIERE);

	TEST_EQUAL(cgm.isValid(), true)
	FF.updateEnergy();
	FF.updateForces();
	result = cgm.minimize(100);

	TEST_EQUAL(result, true)
	float energy = FF.updateEnergy();
	FF.updateForces();

	PRECISION(1E-3)
	TEST_REAL_EQUAL(energy, 5.906)

	AtomIterator atit;
	Vector3 pos[8];
	int i=0;

	for (atit = S.beginAtom(); +atit; ++atit)
	{
		pos[i] = atit->getPosition();
		++i;
	}

	PRECISION(1E-3)
	Angle torsion;
	torsion = getTorsionAngle(pos[2].x, pos[2].y, pos[2].z, pos[0].x, pos[0].y, pos[0].z, pos[1].x, pos[1].y, pos[1].z, pos[6].x, pos[6].y, pos[6].z);
	TEST_REAL_EQUAL(torsion.toRadian(), 1.047)
	torsion = getTorsionAngle(pos[3].x, pos[3].y, pos[3].z, pos[0].x, pos[0].y, pos[0].z, pos[1].x, pos[1].y, pos[1].z, pos[5].x, pos[5].y, pos[5].z);
	TEST_REAL_EQUAL(torsion.toRadian(), 1.047)
	torsion = getTorsionAngle(pos[4].x, pos[4].y, pos[4].z, pos[0].x, pos[0].y, pos[0].z, pos[1].x, pos[1].y, pos[1].z, pos[7].x, pos[7].y, pos[7].z);
	TEST_REAL_EQUAL(torsion.toRadian(), 1.047)

	PRECISION(1E-2)
	Angle tet;
	tet = (pos[2] - pos[0]).getAngle(pos[3] - pos[0]);
	TEST_REAL_EQUAL(tet.toRadian(), 1.902)
	tet = (pos[2] - pos[0]).getAngle(pos[4] - pos[0]);
	TEST_REAL_EQUAL(tet.toRadian(), 1.902)
	tet = (pos[4] - pos[0]).getAngle(pos[3] - pos[0]);
	TEST_REAL_EQUAL(tet.toRadian(), 1.902)
	tet = (pos[5] - pos[1]).getAngle(pos[6] - pos[1]);
	TEST_REAL_EQUAL(tet.toRadian(), 1.902)
	tet = (pos[5] - pos[1]).getAngle(pos[7] - pos[1]);
	TEST_REAL_EQUAL(tet.toRadian(), 1.902)
	tet = (pos[7] - pos[1]).getAngle(pos[6] - pos[1]);
	TEST_REAL_EQUAL(tet.toRadian(), 1.902)

RESULT

/////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////

END_TEST
<|endoftext|>


<commit_before>// Copyright (c) 2006-2008 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 "chrome/browser/printing/print_job_worker.h"

#include "base/message_loop.h"
#include "chrome/browser/printing/print_job.h"
#include "chrome/common/notification_service.h"
#include "printing/printed_document.h"
#include "printing/printed_page.h"

namespace printing {

class PrintJobWorker::NotificationTask : public Task {
 public:
  NotificationTask() : print_job_(NULL), details_(NULL) {
  }
  ~NotificationTask() {
  }

  // Initializes the object. This object can't be initialized in the constructor
  // since it is not created directly.
  void Init(PrintJobWorkerOwner* print_job,
            JobEventDetails::Type detail_type,
            PrintedDocument* document,
            PrintedPage* page) {
    DCHECK(!print_job_);
    DCHECK(!details_);
    print_job_ = print_job;
    details_ = new JobEventDetails(detail_type, document, page);
  }

  virtual void Run() {
    // Send the notification in the right thread.
    NotificationService::current()->Notify(
        NotificationType::PRINT_JOB_EVENT,
        // We know that is is a PrintJob object in this circumstance.
        Source<PrintJob>(static_cast<PrintJob*>(print_job_.get())),
        Details<JobEventDetails>(details_));
  }

  // The job which originates this notification.
  scoped_refptr<PrintJobWorkerOwner> print_job_;
  scoped_refptr<JobEventDetails> details_;
};

PrintJobWorker::PrintJobWorker(PrintJobWorkerOwner* owner)
    : Thread("Printing_Worker"),
      owner_(owner) {
  // The object is created in the IO thread.
  DCHECK_EQ(owner_->message_loop(), MessageLoop::current());
}

PrintJobWorker::~PrintJobWorker() {
  // The object is deleted in the UI thread.
  DCHECK_EQ(owner_->message_loop(), MessageLoop::current());
}

void PrintJobWorker::SetNewOwner(PrintJobWorkerOwner* new_owner) {
  DCHECK(page_number_ == PageNumber::npos());
  owner_ = new_owner;
}

void PrintJobWorker::GetSettings(bool ask_user_for_settings,
                                 HWND parent_window,
                                 int document_page_count,
                                 bool has_selection) {
  DCHECK_EQ(message_loop(), MessageLoop::current());
  DCHECK_EQ(page_number_, PageNumber::npos());

  // Recursive task processing is needed for the dialog in case it needs to be
  // destroyed by a task.
  MessageLoop::current()->SetNestableTasksAllowed(true);

  PrintingContext::Result result;
  if (ask_user_for_settings) {
    result = printing_context_.AskUserForSettings(parent_window,
                                                  document_page_count,
                                                  has_selection);
  } else {
    result = printing_context_.UseDefaultSettings();
  }

  // Most PrintingContext functions may start a message loop and process
  // message recursively, so disable recursive task processing.
  MessageLoop::current()->SetNestableTasksAllowed(false);

  // We can't use OnFailure() here since owner_ may not support notifications.

  // PrintJob will create the new PrintedDocument.
  owner_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
      owner_,
      &PrintJobWorkerOwner::GetSettingsDone,
      printing_context_.settings(),
      result));
}

void PrintJobWorker::StartPrinting(PrintedDocument* new_document) {
  DCHECK_EQ(message_loop(), MessageLoop::current());
  DCHECK_EQ(page_number_, PageNumber::npos());
  DCHECK_EQ(document_, new_document);
  DCHECK(document_.get());
  DCHECK(new_document->settings().Equals(printing_context_.settings()));
  DCHECK(printing_context_.context());
  if (!document_.get() || page_number_ != PageNumber::npos() ||
      document_ != new_document) {
    return;
  }

  PrintingContext::Result result =
      printing_context_.NewDocument(document_->name());
  if (result != PrintingContext::OK) {
    OnFailure();
    return;
  }

  // Try to print already cached data. It may already have been generated for
  // the print preview.
  OnNewPage();
  // Don't touch this anymore since the instance could be destroyed. It happens
  // if all the pages are printed a one sweep and the client doesn't have a
  // handle to us anymore. There's a timing issue involved between the worker
  // thread and the UI thread. Take no chance.
}

void PrintJobWorker::OnDocumentChanged(PrintedDocument* new_document) {
  DCHECK_EQ(message_loop(), MessageLoop::current());
  DCHECK_EQ(page_number_, PageNumber::npos());
  DCHECK(!new_document ||
         new_document->settings().Equals(printing_context_.settings()));
  DCHECK(printing_context_.context());
  if (page_number_ != PageNumber::npos())
    return;

  document_ = new_document;
}

void PrintJobWorker::OnNewPage() {
  if (!document_.get()) {
    // Spurious message.
    return;
  }
  // message_loop() could return NULL when the print job is cancelled.
  DCHECK_EQ(message_loop(), MessageLoop::current());
  DCHECK(printing_context_.context());
  if (!printing_context_.context())
    return;

  if (page_number_ == PageNumber::npos()) {
    // Find first page to print.
    int page_count = document_->page_count();
    if (!page_count) {
      // We still don't know how many pages the document contains. We can't
      // start to print the document yet since the header/footer may refer to
      // the document's page count.
      return;
    }
    // We have enough information to initialize page_number_.
    page_number_.Init(document_->settings(), page_count);
  }
  DCHECK_NE(page_number_, PageNumber::npos());

  for (;;) {
    // Is the page available?
    scoped_refptr<PrintedPage> page;
    if (!document_->GetPage(page_number_.ToInt(), &page)) {
      // The page is implicitly requested.
      break;
    }
    // The page is there, print it.
    SpoolPage(*page);
    ++page_number_;
    if (page_number_ == PageNumber::npos()) {
      OnDocumentDone();
      // Don't touch this anymore since the instance could be destroyed.
      break;
    }
  }
}

void PrintJobWorker::Cancel() {
  // This is the only function that can be called from any thread.
  printing_context_.Cancel();
  // Cannot touch any member variable since we don't know in which thread
  // context we run.
}

void PrintJobWorker::DismissDialog() {
  printing_context_.DismissDialog();
}

void PrintJobWorker::OnDocumentDone() {
  DCHECK_EQ(message_loop(), MessageLoop::current());
  DCHECK_EQ(page_number_, PageNumber::npos());
  DCHECK(document_.get());
  DCHECK(printing_context_.context());

  if (printing_context_.DocumentDone() != PrintingContext::OK) {
    OnFailure();
    return;
  }

  // Tell everyone!
  NotificationTask* task = new NotificationTask();
  task->Init(owner_,
             JobEventDetails::DOC_DONE,
             document_.get(),
             NULL);
  owner_->message_loop()->PostTask(FROM_HERE, task);

  // Makes sure the variables are reinitialized.
  document_ = NULL;
}

void PrintJobWorker::SpoolPage(PrintedPage& page) {
  DCHECK_EQ(message_loop(), MessageLoop::current());
  DCHECK_NE(page_number_, PageNumber::npos());
  DCHECK(printing_context_.context());
  // Signal everyone that the page is about to be printed.
  NotificationTask* task = new NotificationTask();
  task->Init(owner_,
             JobEventDetails::NEW_PAGE,
             document_.get(),
             &page);
  owner_->message_loop()->PostTask(FROM_HERE, task);

  // Preprocess.
  if (printing_context_.NewPage() != PrintingContext::OK) {
    OnFailure();
    return;
  }

  // Actual printing.
  document_->RenderPrintedPage(page, printing_context_.context());

  // Postprocess.
  if (printing_context_.PageDone() != PrintingContext::OK) {
    OnFailure();
    return;
  }

  // Signal everyone that the page is printed.
  task = new NotificationTask();
  task->Init(owner_,
             JobEventDetails::PAGE_DONE,
             document_.get(),
             &page);
  owner_->message_loop()->PostTask(FROM_HERE, task);
}

void PrintJobWorker::OnFailure() {
  DCHECK_EQ(message_loop(), MessageLoop::current());

  // We may loose our last reference by broadcasting the FAILED event.
  scoped_refptr<PrintJobWorkerOwner> handle(owner_);

  NotificationTask* task = new NotificationTask();
  task->Init(owner_,
             JobEventDetails::FAILED,
             document_.get(),
             NULL);
  owner_->message_loop()->PostTask(FROM_HERE, task);
  Cancel();

  // Makes sure the variables are reinitialized.
  document_ = NULL;
  page_number_ = PageNumber::npos();
}

}  // namespace printing

RunnableMethodTraits<printing::PrintJobWorker>::RunnableMethodTraits() {
}

void RunnableMethodTraits<printing::PrintJobWorker>::RetainCallee(
    printing::PrintJobWorker* obj) {
  DCHECK(!owner_.get());
  owner_ = obj->owner_;
}

void RunnableMethodTraits<printing::PrintJobWorker>::ReleaseCallee(
    printing::PrintJobWorker* obj) {
  DCHECK_EQ(owner_, obj->owner_);
  owner_ = NULL;
}
<commit_msg>I've seen recently a race condition while debugging printing.  The pages are not sent from the renderer quickly enough which causes the print thread to hang (no new page notifications are sent any more). This change simply adds a poll every half a second for a new page.<commit_after>// Copyright (c) 2006-2008 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 "chrome/browser/printing/print_job_worker.h"

#include "base/message_loop.h"
#include "chrome/browser/printing/print_job.h"
#include "chrome/common/notification_service.h"
#include "printing/printed_document.h"
#include "printing/printed_page.h"

namespace printing {

class PrintJobWorker::NotificationTask : public Task {
 public:
  NotificationTask() : print_job_(NULL), details_(NULL) {
  }
  ~NotificationTask() {
  }

  // Initializes the object. This object can't be initialized in the constructor
  // since it is not created directly.
  void Init(PrintJobWorkerOwner* print_job,
            JobEventDetails::Type detail_type,
            PrintedDocument* document,
            PrintedPage* page) {
    DCHECK(!print_job_);
    DCHECK(!details_);
    print_job_ = print_job;
    details_ = new JobEventDetails(detail_type, document, page);
  }

  virtual void Run() {
    // Send the notification in the right thread.
    NotificationService::current()->Notify(
        NotificationType::PRINT_JOB_EVENT,
        // We know that is is a PrintJob object in this circumstance.
        Source<PrintJob>(static_cast<PrintJob*>(print_job_.get())),
        Details<JobEventDetails>(details_));
  }

  // The job which originates this notification.
  scoped_refptr<PrintJobWorkerOwner> print_job_;
  scoped_refptr<JobEventDetails> details_;
};

PrintJobWorker::PrintJobWorker(PrintJobWorkerOwner* owner)
    : Thread("Printing_Worker"),
      owner_(owner) {
  // The object is created in the IO thread.
  DCHECK_EQ(owner_->message_loop(), MessageLoop::current());
}

PrintJobWorker::~PrintJobWorker() {
  // The object is deleted in the UI thread.
  DCHECK_EQ(owner_->message_loop(), MessageLoop::current());
}

void PrintJobWorker::SetNewOwner(PrintJobWorkerOwner* new_owner) {
  DCHECK(page_number_ == PageNumber::npos());
  owner_ = new_owner;
}

void PrintJobWorker::GetSettings(bool ask_user_for_settings,
                                 HWND parent_window,
                                 int document_page_count,
                                 bool has_selection) {
  DCHECK_EQ(message_loop(), MessageLoop::current());
  DCHECK_EQ(page_number_, PageNumber::npos());

  // Recursive task processing is needed for the dialog in case it needs to be
  // destroyed by a task.
  MessageLoop::current()->SetNestableTasksAllowed(true);

  PrintingContext::Result result;
  if (ask_user_for_settings) {
    result = printing_context_.AskUserForSettings(parent_window,
                                                  document_page_count,
                                                  has_selection);
  } else {
    result = printing_context_.UseDefaultSettings();
  }

  // Most PrintingContext functions may start a message loop and process
  // message recursively, so disable recursive task processing.
  MessageLoop::current()->SetNestableTasksAllowed(false);

  // We can't use OnFailure() here since owner_ may not support notifications.

  // PrintJob will create the new PrintedDocument.
  owner_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
      owner_,
      &PrintJobWorkerOwner::GetSettingsDone,
      printing_context_.settings(),
      result));
}

void PrintJobWorker::StartPrinting(PrintedDocument* new_document) {
  DCHECK_EQ(message_loop(), MessageLoop::current());
  DCHECK_EQ(page_number_, PageNumber::npos());
  DCHECK_EQ(document_, new_document);
  DCHECK(document_.get());
  DCHECK(new_document->settings().Equals(printing_context_.settings()));
  DCHECK(printing_context_.context());
  if (!document_.get() || page_number_ != PageNumber::npos() ||
      document_ != new_document) {
    return;
  }

  PrintingContext::Result result =
      printing_context_.NewDocument(document_->name());
  if (result != PrintingContext::OK) {
    OnFailure();
    return;
  }

  // Try to print already cached data. It may already have been generated for
  // the print preview.
  OnNewPage();
  // Don't touch this anymore since the instance could be destroyed. It happens
  // if all the pages are printed a one sweep and the client doesn't have a
  // handle to us anymore. There's a timing issue involved between the worker
  // thread and the UI thread. Take no chance.
}

void PrintJobWorker::OnDocumentChanged(PrintedDocument* new_document) {
  DCHECK_EQ(message_loop(), MessageLoop::current());
  DCHECK_EQ(page_number_, PageNumber::npos());
  DCHECK(!new_document ||
         new_document->settings().Equals(printing_context_.settings()));
  DCHECK(printing_context_.context());
  if (page_number_ != PageNumber::npos())
    return;

  document_ = new_document;
}

void PrintJobWorker::OnNewPage() {
  if (!document_.get()) {
    // Spurious message.
    return;
  }
  // message_loop() could return NULL when the print job is cancelled.
  DCHECK_EQ(message_loop(), MessageLoop::current());
  DCHECK(printing_context_.context());
  if (!printing_context_.context())
    return;

  if (page_number_ == PageNumber::npos()) {
    // Find first page to print.
    int page_count = document_->page_count();
    if (!page_count) {
      // We still don't know how many pages the document contains. We can't
      // start to print the document yet since the header/footer may refer to
      // the document's page count.
      return;
    }
    // We have enough information to initialize page_number_.
    page_number_.Init(document_->settings(), page_count);
  }
  DCHECK_NE(page_number_, PageNumber::npos());

  for (;;) {
    // Is the page available?
    scoped_refptr<PrintedPage> page;
    if (!document_->GetPage(page_number_.ToInt(), &page)) {
      // We need to wait for the page to be available.
      MessageLoop::current()->PostDelayedTask(
          FROM_HERE,
          NewRunnableMethod(this, &PrintJobWorker::OnNewPage),
          500);
      break;
    }
    // The page is there, print it.
    SpoolPage(*page);
    ++page_number_;
    if (page_number_ == PageNumber::npos()) {
      OnDocumentDone();
      // Don't touch this anymore since the instance could be destroyed.
      break;
    }
  }
}

void PrintJobWorker::Cancel() {
  // This is the only function that can be called from any thread.
  printing_context_.Cancel();
  // Cannot touch any member variable since we don't know in which thread
  // context we run.
}

void PrintJobWorker::DismissDialog() {
  printing_context_.DismissDialog();
}

void PrintJobWorker::OnDocumentDone() {
  DCHECK_EQ(message_loop(), MessageLoop::current());
  DCHECK_EQ(page_number_, PageNumber::npos());
  DCHECK(document_.get());
  DCHECK(printing_context_.context());

  if (printing_context_.DocumentDone() != PrintingContext::OK) {
    OnFailure();
    return;
  }

  // Tell everyone!
  NotificationTask* task = new NotificationTask();
  task->Init(owner_,
             JobEventDetails::DOC_DONE,
             document_.get(),
             NULL);
  owner_->message_loop()->PostTask(FROM_HERE, task);

  // Makes sure the variables are reinitialized.
  document_ = NULL;
}

void PrintJobWorker::SpoolPage(PrintedPage& page) {
  DCHECK_EQ(message_loop(), MessageLoop::current());
  DCHECK_NE(page_number_, PageNumber::npos());
  DCHECK(printing_context_.context());
  // Signal everyone that the page is about to be printed.
  NotificationTask* task = new NotificationTask();
  task->Init(owner_,
             JobEventDetails::NEW_PAGE,
             document_.get(),
             &page);
  owner_->message_loop()->PostTask(FROM_HERE, task);

  // Preprocess.
  if (printing_context_.NewPage() != PrintingContext::OK) {
    OnFailure();
    return;
  }

  // Actual printing.
  document_->RenderPrintedPage(page, printing_context_.context());

  // Postprocess.
  if (printing_context_.PageDone() != PrintingContext::OK) {
    OnFailure();
    return;
  }

  // Signal everyone that the page is printed.
  task = new NotificationTask();
  task->Init(owner_,
             JobEventDetails::PAGE_DONE,
             document_.get(),
             &page);
  owner_->message_loop()->PostTask(FROM_HERE, task);
}

void PrintJobWorker::OnFailure() {
  DCHECK_EQ(message_loop(), MessageLoop::current());

  // We may loose our last reference by broadcasting the FAILED event.
  scoped_refptr<PrintJobWorkerOwner> handle(owner_);

  NotificationTask* task = new NotificationTask();
  task->Init(owner_,
             JobEventDetails::FAILED,
             document_.get(),
             NULL);
  owner_->message_loop()->PostTask(FROM_HERE, task);
  Cancel();

  // Makes sure the variables are reinitialized.
  document_ = NULL;
  page_number_ = PageNumber::npos();
}

}  // namespace printing

RunnableMethodTraits<printing::PrintJobWorker>::RunnableMethodTraits() {
}

void RunnableMethodTraits<printing::PrintJobWorker>::RetainCallee(
    printing::PrintJobWorker* obj) {
  DCHECK(!owner_.get());
  owner_ = obj->owner_;
}

void RunnableMethodTraits<printing::PrintJobWorker>::ReleaseCallee(
    printing::PrintJobWorker* obj) {
  DCHECK_EQ(owner_, obj->owner_);
  owner_ = NULL;
}
<|endoftext|>