<commit_before>/// \file ROOT/RColumn.hxx
/// \ingroup NTuple ROOT7
/// \author Jakob Blomer <jblomer@cern.ch>
/// \date 2018-10-09
/// \warning This is part of the ROOT 7 prototype! It will change without notice. It might trigger earthquakes. Feedback
/// is welcome!

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

#ifndef ROOT7_RColumn
#define ROOT7_RColumn

#include <ROOT/RColumnElement.hxx>
#include <ROOT/RColumnModel.hxx>
#include <ROOT/RNTupleUtil.hxx>
#include <ROOT/RPage.hxx>
#include <ROOT/RPageStorage.hxx>

#include <TError.h>

#include <memory>

namespace ROOT {
namespace Experimental {
namespace Detail {

// clang-format off
/**
\class ROOT::Experimental::RColumn
\ingroup NTuple
\brief A column is a storage-backed array of a simple, fixed-size type, from which pages can be mapped into memory.

On the primitives data layer, the RColumn and RColumnElement are the equivalents to RField and RFieldValue on the
logical data layer.
*/
// clang-format on
class RColumn {
private:
   RColumnModel fModel;
   /**
    * Columns belonging to the same field are distinguished by their order.  E.g. for an std::string field, there is
    * the offset column with index 0 and the character value column with index 1.
    */
   std::uint32_t fIndex;
   RPageSink *fPageSink = nullptr;
   RPageSource *fPageSource = nullptr;
   RPageStorage::ColumnHandle_t fHandleSink;
   RPageStorage::ColumnHandle_t fHandleSource;
   /// A set of open pages into which new elements are being written. The pages are used
   /// in rotation. They are 50% bigger than the target size given by the write options.
   /// The current page is filled until the target size, but it is only committed once the other
   /// head page is filled at least 50%. If a flush occurs earlier, a slightly oversize, single
   /// page will be committed.
   RPage fHeadPage[2];
   /// Index of the current head page
   int fHeadPageIdx = 0;
   /// For writing, the targeted number of elements, given by `fApproxNElementsPerPage` (in the write options) and the element size.
   /// We ensure this value to be >= 2 in Connect() so that we have meaningful
   /// "page full" and "page half full" events when writing the page.
   std::uint32_t fApproxNElementsPerPage = 0;
   /// The number of elements written resp. available in the column
   NTupleSize_t fNElements = 0;
   /// The currently mapped page for reading
   RPage fCurrentPage;
   /// The column id is used to find matching pages with content when reading
   ColumnId_t fColumnIdSource = kInvalidColumnId;
   /// Used to pack and unpack pages on writing/reading
   std::unique_ptr<RColumnElementBase> fElement;

   RColumn(const RColumnModel &model, std::uint32_t index);

   /// Used in Append() and AppendV() to switch pages when the main page reached the target size
   /// The other page has been flushed when the main page reached 50%.
   void SwapHeadPages() {
      fHeadPageIdx = 1 - fHeadPageIdx; // == (fHeadPageIdx + 1) % 2
      R__ASSERT(fHeadPage[fHeadPageIdx].IsEmpty());
      fHeadPage[fHeadPageIdx].Reset(fNElements);
   }

   /// When the main head page surpasses the 50% fill level, the (full) shadow head page gets flushed
   void FlushShadowHeadPage() {
      auto otherIdx = 1 - fHeadPageIdx;
      if (fHeadPage[otherIdx].IsEmpty())
         return;
      fPageSink->CommitPage(fHandleSink, fHeadPage[otherIdx]);
      // Mark the page as flushed; the rangeFirst is zero for now but will be reset to
      // fNElements in SwapHeadPages() when the pages swap
      fHeadPage[otherIdx].Reset(0);
   }

public:
   template <typename CppT, EColumnType ColumnT>
   static RColumn *Create(const RColumnModel &model, std::uint32_t index) {
      R__ASSERT(model.GetType() == ColumnT);
      auto column = new RColumn(model, index);
      column->fElement = std::unique_ptr<RColumnElementBase>(new RColumnElement<CppT, ColumnT>(nullptr));
      return column;
   }

   RColumn(const RColumn&) = delete;
   RColumn &operator =(const RColumn&) = delete;
   ~RColumn();

   void Connect(DescriptorId_t fieldId, RPageStorage *pageStorage);

   void Append(const RColumnElementBase &element) {
      void *dst = fHeadPage[fHeadPageIdx].GrowUnchecked(1);

      if (fHeadPage[fHeadPageIdx].GetNElements() == fApproxNElementsPerPage / 2) {
         FlushShadowHeadPage();
      }

      element.WriteTo(dst, 1);
      fNElements++;

      if (fHeadPage[fHeadPageIdx].GetNElements() == fApproxNElementsPerPage)
         SwapHeadPages();
   }

   void AppendV(const RColumnElementBase &elemArray, std::size_t count) {
      // We might not have enough space in the current page. In this case, fall back to one by one filling.
      if (fHeadPage[fHeadPageIdx].GetNElements() + count > fApproxNElementsPerPage) {
         // TODO(jblomer): use (fewer) calls to AppendV to write the data page-by-page
         for (unsigned i = 0; i < count; ++i) {
            Append(RColumnElementBase(elemArray, i));
         }
         return;
      }

      void *dst = fHeadPage[fHeadPageIdx].GrowUnchecked(count);

      // The check for flushing the shadow page is more complicated than for the Append() case
      // because we don't necessarily fill up to exactly fApproxNElementsPerPage / 2 elements;
      // we might instead jump over the 50% fill level
      if ((fHeadPage[fHeadPageIdx].GetNElements() <= fApproxNElementsPerPage / 2) &&
          (fHeadPage[fHeadPageIdx].GetNElements() + count > fApproxNElementsPerPage / 2))
      {
         FlushShadowHeadPage();
      }

      elemArray.WriteTo(dst, count);
      fNElements += count;

      // Note that by the very first check, we cannot have filled more than fApproxNElementsPerPage elements
      if (fHeadPage[fHeadPageIdx].GetNElements() == fApproxNElementsPerPage)
         SwapHeadPages();
   }

   void Read(const NTupleSize_t globalIndex, RColumnElementBase *element) {
      if (!fCurrentPage.Contains(globalIndex)) {
         MapPage(globalIndex);
         R__ASSERT(fCurrentPage.Contains(globalIndex));
      }
      void *src = static_cast<unsigned char *>(fCurrentPage.GetBuffer()) +
                  (globalIndex - fCurrentPage.GetGlobalRangeFirst()) * element->GetSize();
      element->ReadFrom(src, 1);
   }

   void Read(const RClusterIndex &clusterIndex, RColumnElementBase *element) {
      if (!fCurrentPage.Contains(clusterIndex)) {
         MapPage(clusterIndex);
      }
      void *src = static_cast<unsigned char *>(fCurrentPage.GetBuffer()) +
                  (clusterIndex.GetIndex() - fCurrentPage.GetClusterRangeFirst()) * element->GetSize();
      element->ReadFrom(src, 1);
   }

   void ReadV(const NTupleSize_t globalIndex, const ClusterSize_t::ValueType count, RColumnElementBase *elemArray) {
      R__ASSERT(count > 0);
      if (!fCurrentPage.Contains(globalIndex)) {
         MapPage(globalIndex);
      }
      NTupleSize_t idxInPage = globalIndex - fCurrentPage.GetGlobalRangeFirst();

      void *src = static_cast<unsigned char *>(fCurrentPage.GetBuffer()) + idxInPage * elemArray->GetSize();
      if (globalIndex + count <= fCurrentPage.GetGlobalRangeLast() + 1) {
         elemArray->ReadFrom(src, count);
      } else {
         ClusterSize_t::ValueType nBatch = fCurrentPage.GetNElements() - idxInPage;
         elemArray->ReadFrom(src, nBatch);
         RColumnElementBase elemTail(*elemArray, nBatch);
         ReadV(globalIndex + nBatch, count - nBatch, &elemTail);
      }
   }

   void ReadV(const RClusterIndex &clusterIndex, const ClusterSize_t::ValueType count, RColumnElementBase *elemArray)
   {
      if (!fCurrentPage.Contains(clusterIndex)) {
         MapPage(clusterIndex);
      }
      NTupleSize_t idxInPage = clusterIndex.GetIndex() - fCurrentPage.GetClusterRangeFirst();

      void* src = static_cast<unsigned char *>(fCurrentPage.GetBuffer()) + idxInPage * elemArray->GetSize();
      if (clusterIndex.GetIndex() + count <= fCurrentPage.GetClusterRangeLast() + 1) {
         elemArray->ReadFrom(src, count);
      } else {
         ClusterSize_t::ValueType nBatch = fCurrentPage.GetNElements() - idxInPage;
         elemArray->ReadFrom(src, nBatch);
         RColumnElementBase elemTail(*elemArray, nBatch);
         ReadV(RClusterIndex(clusterIndex.GetClusterId(), clusterIndex.GetIndex() + nBatch), count - nBatch, &elemTail);
      }
   }

   template <typename CppT>
   CppT *Map(const NTupleSize_t globalIndex) {
      NTupleSize_t nItems;
      return MapV<CppT>(globalIndex, nItems);
   }

   template <typename CppT>
   CppT *Map(const RClusterIndex &clusterIndex) {
      NTupleSize_t nItems;
      return MapV<CppT>(clusterIndex, nItems);
   }

   template <typename CppT>
   CppT *MapV(const NTupleSize_t globalIndex, NTupleSize_t &nItems) {
      if (!fCurrentPage.Contains(globalIndex)) {
         MapPage(globalIndex);
      }
      // +1 to go from 0-based indexing to 1-based number of items
      nItems = fCurrentPage.GetGlobalRangeLast() - globalIndex + 1;
      return reinterpret_cast<CppT*>(
         static_cast<unsigned char *>(fCurrentPage.GetBuffer()) +
         (globalIndex - fCurrentPage.GetGlobalRangeFirst()) * RColumnElement<CppT>::kSize);
   }

   template <typename CppT>
   CppT *MapV(const RClusterIndex &clusterIndex, NTupleSize_t &nItems) {
      if (!fCurrentPage.Contains(clusterIndex)) {
         MapPage(clusterIndex);
      }
      // +1 to go from 0-based indexing to 1-based number of items
      nItems = fCurrentPage.GetClusterRangeLast() - clusterIndex.GetIndex() + 1;
      return reinterpret_cast<CppT*>(
         static_cast<unsigned char *>(fCurrentPage.GetBuffer()) +
         (clusterIndex.GetIndex() - fCurrentPage.GetClusterRangeFirst()) * RColumnElement<CppT>::kSize);
   }

   NTupleSize_t GetGlobalIndex(const RClusterIndex &clusterIndex) {
      if (!fCurrentPage.Contains(clusterIndex)) {
         MapPage(clusterIndex);
      }
      return fCurrentPage.GetClusterInfo().GetIndexOffset() + clusterIndex.GetIndex();
   }

   RClusterIndex GetClusterIndex(NTupleSize_t globalIndex) {
      if (!fCurrentPage.Contains(globalIndex)) {
         MapPage(globalIndex);
      }
      return RClusterIndex(fCurrentPage.GetClusterInfo().GetId(),
                           globalIndex - fCurrentPage.GetClusterInfo().GetIndexOffset());
   }

   /// For offset columns only, look at the two adjacent values that define a collection's coordinates
   void GetCollectionInfo(const NTupleSize_t globalIndex, RClusterIndex *collectionStart, ClusterSize_t *collectionSize)
   {
      auto idxStart = (globalIndex == 0) ? 0 : *Map<ClusterSize_t>(globalIndex - 1);
      auto idxEnd = *Map<ClusterSize_t>(globalIndex);
      auto selfOffset = fCurrentPage.GetClusterInfo().GetIndexOffset();
      if (globalIndex == selfOffset) {
         // Passed cluster boundary
         idxStart = 0;
      }
      *collectionSize = idxEnd - idxStart;
      *collectionStart = RClusterIndex(fCurrentPage.GetClusterInfo().GetId(), idxStart);
   }

   void GetCollectionInfo(const RClusterIndex &clusterIndex,
                          RClusterIndex *collectionStart, ClusterSize_t *collectionSize)
   {
      auto index = clusterIndex.GetIndex();
      auto idxStart = (index == 0) ? 0 : *Map<ClusterSize_t>(clusterIndex - 1);
      auto idxEnd = *Map<ClusterSize_t>(clusterIndex);
      *collectionSize = idxEnd - idxStart;
      *collectionStart = RClusterIndex(clusterIndex.GetClusterId(), idxStart);
   }

   /// Get the currently active cluster id
   void GetSwitchInfo(NTupleSize_t globalIndex, RClusterIndex *varIndex, std::uint32_t *tag) {
      auto varSwitch = Map<RColumnSwitch>(globalIndex);
      *varIndex = RClusterIndex(fCurrentPage.GetClusterInfo().GetId(), varSwitch->GetIndex());
      *tag = varSwitch->GetTag();
   }

   void Flush();
   void MapPage(const NTupleSize_t index);
   void MapPage(const RClusterIndex &clusterIndex);
   NTupleSize_t GetNElements() const { return fNElements; }
   RColumnElementBase *GetElement() const { return fElement.get(); }
   const RColumnModel &GetModel() const { return fModel; }
   std::uint32_t GetIndex() const { return fIndex; }
   ColumnId_t GetColumnIdSource() const { return fColumnIdSource; }
   RPageSource *GetPageSource() const { return fPageSource; }
   RPageStorage::ColumnHandle_t GetHandleSource() const { return fHandleSource; }
   RPageStorage::ColumnHandle_t GetHandleSink() const { return fHandleSink; }
   RNTupleVersion GetVersion() const { return RNTupleVersion(); }
};

} // namespace Detail

} // namespace Experimental
} // namespace ROOT

#endif
<commit_msg>[ntuple, NFC] Fix typo in code comment<commit_after>/// \file ROOT/RColumn.hxx
/// \ingroup NTuple ROOT7
/// \author Jakob Blomer <jblomer@cern.ch>
/// \date 2018-10-09
/// \warning This is part of the ROOT 7 prototype! It will change without notice. It might trigger earthquakes. Feedback
/// is welcome!

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

#ifndef ROOT7_RColumn
#define ROOT7_RColumn

#include <ROOT/RColumnElement.hxx>
#include <ROOT/RColumnModel.hxx>
#include <ROOT/RNTupleUtil.hxx>
#include <ROOT/RPage.hxx>
#include <ROOT/RPageStorage.hxx>

#include <TError.h>

#include <memory>

namespace ROOT {
namespace Experimental {
namespace Detail {

// clang-format off
/**
\class ROOT::Experimental::RColumn
\ingroup NTuple
\brief A column is a storage-backed array of a simple, fixed-size type, from which pages can be mapped into memory.

On the primitives data layer, the RColumn and RColumnElement are the equivalents to RField and RFieldValue on the
logical data layer.
*/
// clang-format on
class RColumn {
private:
   RColumnModel fModel;
   /**
    * Columns belonging to the same field are distinguished by their order.  E.g. for an std::string field, there is
    * the offset column with index 0 and the character value column with index 1.
    */
   std::uint32_t fIndex;
   RPageSink *fPageSink = nullptr;
   RPageSource *fPageSource = nullptr;
   RPageStorage::ColumnHandle_t fHandleSink;
   RPageStorage::ColumnHandle_t fHandleSource;
   /// A set of open pages into which new elements are being written. The pages are used
   /// in rotation. They are 50% bigger than the target size given by the write options.
   /// The current page is filled until the target size, but it is only committed once the other
   /// head page is filled at least 50%. If a flush occurs earlier, a slightly oversized, single
   /// page will be committed.
   RPage fHeadPage[2];
   /// Index of the current head page
   int fHeadPageIdx = 0;
   /// For writing, the targeted number of elements, given by `fApproxNElementsPerPage` (in the write options) and the element size.
   /// We ensure this value to be >= 2 in Connect() so that we have meaningful
   /// "page full" and "page half full" events when writing the page.
   std::uint32_t fApproxNElementsPerPage = 0;
   /// The number of elements written resp. available in the column
   NTupleSize_t fNElements = 0;
   /// The currently mapped page for reading
   RPage fCurrentPage;
   /// The column id is used to find matching pages with content when reading
   ColumnId_t fColumnIdSource = kInvalidColumnId;
   /// Used to pack and unpack pages on writing/reading
   std::unique_ptr<RColumnElementBase> fElement;

   RColumn(const RColumnModel &model, std::uint32_t index);

   /// Used in Append() and AppendV() to switch pages when the main page reached the target size
   /// The other page has been flushed when the main page reached 50%.
   void SwapHeadPages() {
      fHeadPageIdx = 1 - fHeadPageIdx; // == (fHeadPageIdx + 1) % 2
      R__ASSERT(fHeadPage[fHeadPageIdx].IsEmpty());
      fHeadPage[fHeadPageIdx].Reset(fNElements);
   }

   /// When the main head page surpasses the 50% fill level, the (full) shadow head page gets flushed
   void FlushShadowHeadPage() {
      auto otherIdx = 1 - fHeadPageIdx;
      if (fHeadPage[otherIdx].IsEmpty())
         return;
      fPageSink->CommitPage(fHandleSink, fHeadPage[otherIdx]);
      // Mark the page as flushed; the rangeFirst is zero for now but will be reset to
      // fNElements in SwapHeadPages() when the pages swap
      fHeadPage[otherIdx].Reset(0);
   }

public:
   template <typename CppT, EColumnType ColumnT>
   static RColumn *Create(const RColumnModel &model, std::uint32_t index) {
      R__ASSERT(model.GetType() == ColumnT);
      auto column = new RColumn(model, index);
      column->fElement = std::unique_ptr<RColumnElementBase>(new RColumnElement<CppT, ColumnT>(nullptr));
      return column;
   }

   RColumn(const RColumn&) = delete;
   RColumn &operator =(const RColumn&) = delete;
   ~RColumn();

   void Connect(DescriptorId_t fieldId, RPageStorage *pageStorage);

   void Append(const RColumnElementBase &element) {
      void *dst = fHeadPage[fHeadPageIdx].GrowUnchecked(1);

      if (fHeadPage[fHeadPageIdx].GetNElements() == fApproxNElementsPerPage / 2) {
         FlushShadowHeadPage();
      }

      element.WriteTo(dst, 1);
      fNElements++;

      if (fHeadPage[fHeadPageIdx].GetNElements() == fApproxNElementsPerPage)
         SwapHeadPages();
   }

   void AppendV(const RColumnElementBase &elemArray, std::size_t count) {
      // We might not have enough space in the current page. In this case, fall back to one by one filling.
      if (fHeadPage[fHeadPageIdx].GetNElements() + count > fApproxNElementsPerPage) {
         // TODO(jblomer): use (fewer) calls to AppendV to write the data page-by-page
         for (unsigned i = 0; i < count; ++i) {
            Append(RColumnElementBase(elemArray, i));
         }
         return;
      }

      void *dst = fHeadPage[fHeadPageIdx].GrowUnchecked(count);

      // The check for flushing the shadow page is more complicated than for the Append() case
      // because we don't necessarily fill up to exactly fApproxNElementsPerPage / 2 elements;
      // we might instead jump over the 50% fill level
      if ((fHeadPage[fHeadPageIdx].GetNElements() <= fApproxNElementsPerPage / 2) &&
          (fHeadPage[fHeadPageIdx].GetNElements() + count > fApproxNElementsPerPage / 2))
      {
         FlushShadowHeadPage();
      }

      elemArray.WriteTo(dst, count);
      fNElements += count;

      // Note that by the very first check, we cannot have filled more than fApproxNElementsPerPage elements
      if (fHeadPage[fHeadPageIdx].GetNElements() == fApproxNElementsPerPage)
         SwapHeadPages();
   }

   void Read(const NTupleSize_t globalIndex, RColumnElementBase *element) {
      if (!fCurrentPage.Contains(globalIndex)) {
         MapPage(globalIndex);
         R__ASSERT(fCurrentPage.Contains(globalIndex));
      }
      void *src = static_cast<unsigned char *>(fCurrentPage.GetBuffer()) +
                  (globalIndex - fCurrentPage.GetGlobalRangeFirst()) * element->GetSize();
      element->ReadFrom(src, 1);
   }

   void Read(const RClusterIndex &clusterIndex, RColumnElementBase *element) {
      if (!fCurrentPage.Contains(clusterIndex)) {
         MapPage(clusterIndex);
      }
      void *src = static_cast<unsigned char *>(fCurrentPage.GetBuffer()) +
                  (clusterIndex.GetIndex() - fCurrentPage.GetClusterRangeFirst()) * element->GetSize();
      element->ReadFrom(src, 1);
   }

   void ReadV(const NTupleSize_t globalIndex, const ClusterSize_t::ValueType count, RColumnElementBase *elemArray) {
      R__ASSERT(count > 0);
      if (!fCurrentPage.Contains(globalIndex)) {
         MapPage(globalIndex);
      }
      NTupleSize_t idxInPage = globalIndex - fCurrentPage.GetGlobalRangeFirst();

      void *src = static_cast<unsigned char *>(fCurrentPage.GetBuffer()) + idxInPage * elemArray->GetSize();
      if (globalIndex + count <= fCurrentPage.GetGlobalRangeLast() + 1) {
         elemArray->ReadFrom(src, count);
      } else {
         ClusterSize_t::ValueType nBatch = fCurrentPage.GetNElements() - idxInPage;
         elemArray->ReadFrom(src, nBatch);
         RColumnElementBase elemTail(*elemArray, nBatch);
         ReadV(globalIndex + nBatch, count - nBatch, &elemTail);
      }
   }

   void ReadV(const RClusterIndex &clusterIndex, const ClusterSize_t::ValueType count, RColumnElementBase *elemArray)
   {
      if (!fCurrentPage.Contains(clusterIndex)) {
         MapPage(clusterIndex);
      }
      NTupleSize_t idxInPage = clusterIndex.GetIndex() - fCurrentPage.GetClusterRangeFirst();

      void* src = static_cast<unsigned char *>(fCurrentPage.GetBuffer()) + idxInPage * elemArray->GetSize();
      if (clusterIndex.GetIndex() + count <= fCurrentPage.GetClusterRangeLast() + 1) {
         elemArray->ReadFrom(src, count);
      } else {
         ClusterSize_t::ValueType nBatch = fCurrentPage.GetNElements() - idxInPage;
         elemArray->ReadFrom(src, nBatch);
         RColumnElementBase elemTail(*elemArray, nBatch);
         ReadV(RClusterIndex(clusterIndex.GetClusterId(), clusterIndex.GetIndex() + nBatch), count - nBatch, &elemTail);
      }
   }

   template <typename CppT>
   CppT *Map(const NTupleSize_t globalIndex) {
      NTupleSize_t nItems;
      return MapV<CppT>(globalIndex, nItems);
   }

   template <typename CppT>
   CppT *Map(const RClusterIndex &clusterIndex) {
      NTupleSize_t nItems;
      return MapV<CppT>(clusterIndex, nItems);
   }

   template <typename CppT>
   CppT *MapV(const NTupleSize_t globalIndex, NTupleSize_t &nItems) {
      if (!fCurrentPage.Contains(globalIndex)) {
         MapPage(globalIndex);
      }
      // +1 to go from 0-based indexing to 1-based number of items
      nItems = fCurrentPage.GetGlobalRangeLast() - globalIndex + 1;
      return reinterpret_cast<CppT*>(
         static_cast<unsigned char *>(fCurrentPage.GetBuffer()) +
         (globalIndex - fCurrentPage.GetGlobalRangeFirst()) * RColumnElement<CppT>::kSize);
   }

   template <typename CppT>
   CppT *MapV(const RClusterIndex &clusterIndex, NTupleSize_t &nItems) {
      if (!fCurrentPage.Contains(clusterIndex)) {
         MapPage(clusterIndex);
      }
      // +1 to go from 0-based indexing to 1-based number of items
      nItems = fCurrentPage.GetClusterRangeLast() - clusterIndex.GetIndex() + 1;
      return reinterpret_cast<CppT*>(
         static_cast<unsigned char *>(fCurrentPage.GetBuffer()) +
         (clusterIndex.GetIndex() - fCurrentPage.GetClusterRangeFirst()) * RColumnElement<CppT>::kSize);
   }

   NTupleSize_t GetGlobalIndex(const RClusterIndex &clusterIndex) {
      if (!fCurrentPage.Contains(clusterIndex)) {
         MapPage(clusterIndex);
      }
      return fCurrentPage.GetClusterInfo().GetIndexOffset() + clusterIndex.GetIndex();
   }

   RClusterIndex GetClusterIndex(NTupleSize_t globalIndex) {
      if (!fCurrentPage.Contains(globalIndex)) {
         MapPage(globalIndex);
      }
      return RClusterIndex(fCurrentPage.GetClusterInfo().GetId(),
                           globalIndex - fCurrentPage.GetClusterInfo().GetIndexOffset());
   }

   /// For offset columns only, look at the two adjacent values that define a collection's coordinates
   void GetCollectionInfo(const NTupleSize_t globalIndex, RClusterIndex *collectionStart, ClusterSize_t *collectionSize)
   {
      auto idxStart = (globalIndex == 0) ? 0 : *Map<ClusterSize_t>(globalIndex - 1);
      auto idxEnd = *Map<ClusterSize_t>(globalIndex);
      auto selfOffset = fCurrentPage.GetClusterInfo().GetIndexOffset();
      if (globalIndex == selfOffset) {
         // Passed cluster boundary
         idxStart = 0;
      }
      *collectionSize = idxEnd - idxStart;
      *collectionStart = RClusterIndex(fCurrentPage.GetClusterInfo().GetId(), idxStart);
   }

   void GetCollectionInfo(const RClusterIndex &clusterIndex,
                          RClusterIndex *collectionStart, ClusterSize_t *collectionSize)
   {
      auto index = clusterIndex.GetIndex();
      auto idxStart = (index == 0) ? 0 : *Map<ClusterSize_t>(clusterIndex - 1);
      auto idxEnd = *Map<ClusterSize_t>(clusterIndex);
      *collectionSize = idxEnd - idxStart;
      *collectionStart = RClusterIndex(clusterIndex.GetClusterId(), idxStart);
   }

   /// Get the currently active cluster id
   void GetSwitchInfo(NTupleSize_t globalIndex, RClusterIndex *varIndex, std::uint32_t *tag) {
      auto varSwitch = Map<RColumnSwitch>(globalIndex);
      *varIndex = RClusterIndex(fCurrentPage.GetClusterInfo().GetId(), varSwitch->GetIndex());
      *tag = varSwitch->GetTag();
   }

   void Flush();
   void MapPage(const NTupleSize_t index);
   void MapPage(const RClusterIndex &clusterIndex);
   NTupleSize_t GetNElements() const { return fNElements; }
   RColumnElementBase *GetElement() const { return fElement.get(); }
   const RColumnModel &GetModel() const { return fModel; }
   std::uint32_t GetIndex() const { return fIndex; }
   ColumnId_t GetColumnIdSource() const { return fColumnIdSource; }
   RPageSource *GetPageSource() const { return fPageSource; }
   RPageStorage::ColumnHandle_t GetHandleSource() const { return fHandleSource; }
   RPageStorage::ColumnHandle_t GetHandleSink() const { return fHandleSink; }
   RNTupleVersion GetVersion() const { return RNTupleVersion(); }
};

} // namespace Detail

} // namespace Experimental
} // namespace ROOT

#endif
<|endoftext|>


<commit_before>#pragma once

#include <QMainWindow>
#include <QFileDialog>
#include "Game.hpp"

#include "ui_GUI.h"
#include "AugmentedView.hpp"

class VirtualView;

namespace Go_GUI {

class GUI : public QMainWindow
{
    Q_OBJECT
public:
    GUI(QWidget *parent = 0);
    ~GUI(){};

    void init();
    void RenderGame(GoBackend::Game game);

public slots:
    void slot_MenuOpen();
    void slot_MenuSave();
    void slot_MenuInfo();
    void slot_ViewSwitch();

public slots:
    void new_image(const QImage image) {
        printf(">>> New Image arrived! '%d x %d' <<<\n", image.width(), image.height());
        //if (param){
        //	augmented_view->setImage(param);
        //  augmented_view->rescaleImage(augmented_view->parentWidget()->size());
        //}
    }

    void new_game_data(const GoBoard * game_board) {
        auto current_turn = game_board->MoveNumber();
        auto current_player = game_board->ToPlay();
        switch (current_player) {
            case SG_WHITE:
                this->findChild<QLabel* >("white_basket")->setPixmap(whitebasket_pixmap);
                this->findChild<QLabel* >("black_basket")->setPixmap(closedbasket_pixmap);
                break;
            case SG_BLACK:
                this->findChild<QLabel* >("white_basket")->setPixmap(closedbasket_pixmap);
                this->findChild<QLabel* >("black_basket")->setPixmap(blackbasket_pixmap);
                break;
            default:
                assert(false);
                break;
        }

        auto captured_black_stones = game_board->NumPrisoners(SG_BLACK);
        auto captured_white_stones = game_board->NumPrisoners(SG_WHITE);

        this->findChild<QLabel* >("capturedwhite_label")->setText(QString::number(captured_white_stones));
        this->findChild<QLabel* >("capturedblack_label")->setText(QString::number(captured_black_stones));

        printf(">>> New Game data! <<<\n");
    }

    void closeEvent(QCloseEvent *event);

signals:
    void stop_backend_thread();

private:
    Ui::MainWindow ui;
    VirtualView* virtual_view;
    AugmentedView* augmented_view;
    QPixmap whitebasket_pixmap, blackbasket_pixmap, closedbasket_pixmap;
};

} // namespace Go_GUI<commit_msg>sets picture if available<commit_after>#pragma once

#include <QMainWindow>
#include <QFileDialog>
#include "Game.hpp"

#include "ui_GUI.h"
#include "AugmentedView.hpp"

class VirtualView;

namespace Go_GUI {

class GUI : public QMainWindow
{
    Q_OBJECT
public:
    GUI(QWidget *parent = 0);
    ~GUI(){};

    void init();
    void RenderGame(GoBackend::Game game);

public slots:
    void slot_MenuOpen();
    void slot_MenuSave();
    void slot_MenuInfo();
    void slot_ViewSwitch();

public slots:
    void new_image(const QImage image) {
        printf(">>> New Image arrived! '%d x %d' <<<\n", image.width(), image.height());
		if (!image.isNull()){
        	augmented_view->setImage(image);
			augmented_view->rescaleImage(augmented_view->parentWidget()->size());
        }
    }

    void new_game_data(const GoBoard * game_board) {
        auto current_turn = game_board->MoveNumber();
        auto current_player = game_board->ToPlay();
        switch (current_player) {
            case SG_WHITE:
                this->findChild<QLabel* >("white_basket")->setPixmap(whitebasket_pixmap);
                this->findChild<QLabel* >("black_basket")->setPixmap(closedbasket_pixmap);
                break;
            case SG_BLACK:
                this->findChild<QLabel* >("white_basket")->setPixmap(closedbasket_pixmap);
                this->findChild<QLabel* >("black_basket")->setPixmap(blackbasket_pixmap);
                break;
            default:
                assert(false);
                break;
        }

        auto captured_black_stones = game_board->NumPrisoners(SG_BLACK);
        auto captured_white_stones = game_board->NumPrisoners(SG_WHITE);

        this->findChild<QLabel* >("capturedwhite_label")->setText(QString::number(captured_white_stones));
        this->findChild<QLabel* >("capturedblack_label")->setText(QString::number(captured_black_stones));

        printf(">>> New Game data! <<<\n");
    }

    void closeEvent(QCloseEvent *event);

signals:
    void stop_backend_thread();

private:
    Ui::MainWindow ui;
    VirtualView* virtual_view;
    AugmentedView* augmented_view;
    QPixmap whitebasket_pixmap, blackbasket_pixmap, closedbasket_pixmap;
};

} // namespace Go_GUI<|endoftext|>


<commit_before>/*
 * Properties.cpp
 *
 * Copyright 2002, Log4cpp Project. All rights reserved.
 *
 * See the COPYING file for the terms of usage and distribution.
 */

#include "Properties.hh"
#include <cstdlib>

namespace log4cpp {

    Properties::Properties() {
    }

    Properties::~Properties() {
    }

    void Properties::load(std::istream& in) {
        clear();

        std::string fullLine, command;
        std::string leftSide, rightSide;
        char line[256];
        std::string::size_type length;

        while (in.getline(line, 256)) {
            fullLine = line;

            /* if the line contains a # then it is a comment
               if we find it anywhere other than the beginning, then we assume
               there is a command on that line, and it we don't find it at all
               we assume there is a command on the line (we test for valid
               command later) if neither is true, we continue with the next line
            */
            length = fullLine.find('#');
            if (length == std::string::npos) {
                command = fullLine;
            } else if (length > 0) {
                command = fullLine.substr(0, length);
            } else {
                continue;
            }

            // check the command and handle it
            length = command.find('=');
            if (length != std::string::npos) {
                leftSide = command.substr(0, length);
                rightSide = command.substr(length + 1, command.size() - length);
                _substituteVariables(rightSide);
            } else {
                continue;
            }

            /* handle the command by determining what object the left side
               refers to and setting the value given on the right
               ASSUMPTIONS:
               1. first object given  on left side is "log4j" or "log4cpp"
               2. all class names on right side are ignored because we
               probably cannot resolve them anyway.
            */

            // strip off the "log4j" or "log4cpp"
            length = leftSide.find('.');
            if (leftSide.substr(0, length) == "log4j" ||
                leftSide.substr(0, length) == "log4cpp")
                leftSide = leftSide.substr(length + 1);

            // add to the map of properties
            insert(value_type(leftSide, rightSide));
        }
    }

    void Properties::save(std::ostream& out) {
        for(const_iterator i = begin(); i != end(); ++i) {
            out << (*i).first << "=" << (*i).second << std::endl;
        }
    }

    int Properties::getInt(const std::string& property, int defaultValue) {
        const_iterator key = find(property);
        return (key == end()) ? defaultValue : std::atoi((*key).second.c_str());
    }

    bool Properties::getBool(const std::string& property, bool defaultValue) {
        const_iterator key = find(property);
        return (key == end()) ? defaultValue : ((*key).second == "true");
    }

    std::string Properties::getString(const std::string& property,
                                      const char* defaultValue) {
        const_iterator key = find(property);
        return (key == end()) ? std::string(defaultValue) : (*key).second;
    }

    void Properties::_substituteVariables(std::string& value) {
        std::string result;

        std::string::size_type left = 0;
        std::string::size_type right = value.find("${", left);
        if (right == std::string::npos) {
            // bail out early for 99% of cases
            return;
        }

        while(true) {
            result += value.substr(left, right - left);
            if (right == std::string::npos) {
                break;
            }

            left = right + 2;
            right = value.find('}', left);
            if (right == std::string::npos) {
                // no close tag, use string literally
                result += value.substr(left - 2);
                break;
            } else {
                const std::string key = value.substr(left, right - left);
                if (key == "${") {
                    result += "${";
                } else {
                    char* value = std::getenv(key.c_str());
                    if (value) {
                        result += value;
                    } else {
                        const_iterator it = find(key);
                        if (it == end()) {
                            // not found assume empty;
                        } else {
                            result += (*it).second;
                        }
                    }
                }
                left = right + 1;
            }

            right = value.find("${", left);
        }

        value = result;
    }
}
<commit_msg>trim property keys and values. Fixes bug #710164.<commit_after>/*
 * Properties.cpp
 *
 * Copyright 2002, Log4cpp Project. All rights reserved.
 *
 * See the COPYING file for the terms of usage and distribution.
 */

#include "Properties.hh"
#include <cstdlib>
#include "StringUtil.hh"

namespace log4cpp {

    Properties::Properties() {
    }

    Properties::~Properties() {
    }

    void Properties::load(std::istream& in) {
        clear();

        std::string fullLine, command;
        std::string leftSide, rightSide;
        char line[256];
        std::string::size_type length;

        while (in.getline(line, 256)) {
            fullLine = line;

            /* if the line contains a # then it is a comment
               if we find it anywhere other than the beginning, then we assume
               there is a command on that line, and it we don't find it at all
               we assume there is a command on the line (we test for valid
               command later) if neither is true, we continue with the next line
            */
            length = fullLine.find('#');
            if (length == std::string::npos) {
                command = fullLine;
            } else if (length > 0) {
                command = fullLine.substr(0, length);
            } else {
                continue;
            }

            // check the command and handle it
            length = command.find('=');
            if (length != std::string::npos) {
                leftSide = StringUtil::trim(command.substr(0, length));
                rightSide = StringUtil::trim(command.substr(length + 1, command.size() - length));
                _substituteVariables(rightSide);
            } else {
                continue;
            }

            /* handle the command by determining what object the left side
               refers to and setting the value given on the right
               ASSUMPTIONS:
               1. first object given  on left side is "log4j" or "log4cpp"
               2. all class names on right side are ignored because we
               probably cannot resolve them anyway.
            */

            // strip off the "log4j" or "log4cpp"
            length = leftSide.find('.');
            if (leftSide.substr(0, length) == "log4j" ||
                leftSide.substr(0, length) == "log4cpp")
                leftSide = leftSide.substr(length + 1);

            // add to the map of properties
            insert(value_type(leftSide, rightSide));
        }
    }

    void Properties::save(std::ostream& out) {
        for(const_iterator i = begin(); i != end(); ++i) {
            out << (*i).first << "=" << (*i).second << std::endl;
        }
    }

    int Properties::getInt(const std::string& property, int defaultValue) {
        const_iterator key = find(property);
        return (key == end()) ? defaultValue : std::atoi((*key).second.c_str());
    }

    bool Properties::getBool(const std::string& property, bool defaultValue) {
        const_iterator key = find(property);
        return (key == end()) ? defaultValue : ((*key).second == "true");
    }

    std::string Properties::getString(const std::string& property,
                                      const char* defaultValue) {
        const_iterator key = find(property);
        return (key == end()) ? std::string(defaultValue) : (*key).second;
    }

    void Properties::_substituteVariables(std::string& value) {
        std::string result;

        std::string::size_type left = 0;
        std::string::size_type right = value.find("${", left);
        if (right == std::string::npos) {
            // bail out early for 99% of cases
            return;
        }

        while(true) {
            result += value.substr(left, right - left);
            if (right == std::string::npos) {
                break;
            }

            left = right + 2;
            right = value.find('}', left);
            if (right == std::string::npos) {
                // no close tag, use string literally
                result += value.substr(left - 2);
                break;
            } else {
                const std::string key = value.substr(left, right - left);
                if (key == "${") {
                    result += "${";
                } else {
                    char* value = std::getenv(key.c_str());
                    if (value) {
                        result += value;
                    } else {
                        const_iterator it = find(key);
                        if (it == end()) {
                            // not found assume empty;
                        } else {
                            result += (*it).second;
                        }
                    }
                }
                left = right + 1;
            }

            right = value.find("${", left);
        }

        value = result;
    }
}
<|endoftext|>


<commit_before>/*
  Push Server code for CMPT 276 Group Assignment, Spring 2016.

  This server pushes status updates to all a user’s friends in the network.

  This server supports a single operation: push a status update to all friends
  of this user.

  This server handles disallowed method malformed request.
*/

#include <exception>
#include <iostream>
#include <memory>
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>
#include <string>

#include <cpprest/base_uri.h>
#include <cpprest/http_listener.h>
#include <cpprest/json.h>

#include <pplx/pplxtasks.h>

#include <was/common.h>
#include <was/storage_account.h>
#include <was/table.h>

#include "../include/TableCache.h"

#include "../include/make_unique.h"

//#include "../include/azure_keys.h"

#include "../include/ServerUtils.h"
#include "../include/ClientUtils.h"

using azure::storage::cloud_storage_account;
using azure::storage::storage_credentials;
using azure::storage::storage_exception;
using azure::storage::cloud_table;
using azure::storage::cloud_table_client;
using azure::storage::edm_type;
using azure::storage::entity_property;
using azure::storage::table_entity;
using azure::storage::table_operation;
using azure::storage::table_query;
using azure::storage::table_query_iterator;
using azure::storage::table_result;

using pplx::extensibility::critical_section_t;
using pplx::extensibility::scoped_critical_section_t;

using std::cin;
using std::cout;
using std::endl;
using std::getline;
using std::make_pair;
using std::pair;
using std::string;
using std::unordered_map;
using std::vector;

using web::http::http_headers;
using web::http::http_request;
using web::http::methods;
using web::http::status_code;
using web::http::status_codes;
using web::http::uri;

using web::json::value;

using web::http::experimental::listener::http_listener;

//using prop_vals_t = vector<pair<string,value>>;
using friends_list_t = std::vector<std::pair<std::string,std::string>>;

constexpr const char* basic_url = "http://localhost:34568/";
constexpr const char* def_url = "http://localhost:34574/";

const string push_status_op {"PushStatus"};
const string read_entity_admin {"ReadEntityAdmin"};
const string update_entity_admin {"UpdateEntityAdmin"};

const string data_table_name {"DataTable"};

//TableCache table_cache {};

//---------------------------------------------------------------------------------------

/*
  Return true if an HTTP request has a JSON body

  This routine can be called multiple times on the same message.
 */
bool has_json_body (http_request message) {
  return message.headers()["Content-type"] == "application/json";
}

/*
  Given an HTTP message with a JSON body, return the JSON
  body as an unordered map of strings to strings.
  get_json_body and get_json_bourne are valid and identical function calls.

  If the message has no JSON body, return an empty map.

  THIS ROUTINE CAN ONLY BE CALLED ONCE FOR A GIVEN MESSAGE
  (see http://microsoft.github.io/cpprestsdk/classweb_1_1http_1_1http__request.html#ae6c3d7532fe943de75dcc0445456cbc7
  for source of this limit).

  Note that all types of JSON values are returned as strings.
  Use C++ conversion utilities to convert to numbers or dates
  as necessary.
 */
unordered_map<string,string> get_json_body(http_request message) {
  unordered_map<string,string> results {};
  const http_headers& headers {message.headers()};
  auto content_type (headers.find("Content-Type"));
  if (content_type == headers.end() ||
      content_type->second != "application/json")
    return results;

  value json{};
  message.extract_json(true)
    .then([&json](value v) -> bool
          {
            json = v;
            return true;
          })
    .wait();

  if (json.is_object()) {
    for (const auto& v : json.as_object()) {
      if (v.second.is_string()) {
        results[v.first] = v.second.as_string();
      }
      else {
        results[v.first] = v.second.serialize();
      }
    }
  }
  return results;
}

unordered_map<string,string> get_json_bourne(http_request message) {
 return get_json_body(message);
}

//---------------------------------------------------------------------------------------

void handle_post (http_request message) {
  //TODO Not implemented yet!
  //message.reply(status_codes::NotImplemented);

  string path {uri::decode(message.relative_uri().path())};
  cout << endl << "**** POST " << path << endl;
  auto paths = uri::split_path(path);

  // need operation name, country, username, status = 4
  if (paths.size() != 4 ) {
    message.reply(status_codes::BadRequest);
    return;
  }
  //no PushStatus included
  else if(paths[0] != push_status_op) {
    message.reply(status_codes::BadRequest);
    return;
  }
  // no friends body included
  else if(!has_json_body(message)) {
    message.reply(status_codes::BadRequest);
    return;
  }
  unordered_map<string, string> json_body {get_json_bourne(message)};
  unordered_map<string, string>::const_iterator json_body_friends_iterator
    {json_body.find("Friends")};
  //no properties or more than one property
  if(json_body.size() != 1) {
    message.reply(status_codes::BadRequest);
    return;
  }
  // No 'Friends' property
  else if(json_body_friends_iterator == json_body.end()) {
    message.reply(status_codes::BadRequest);
    return;
  }

  if ( json_body_friends_iterator->second.empty() ){
    message.reply(status_codes::BadRequest);
    return;
  }
  vector<pair<string,string>> friends_list { parse_friends_list(json_body_friends_iterator->second) };
  string old_updates;
  string new_updates;
  pair<status_code,value> result;
  vector<pair<string,value>> update_property;

  for ( int i = 0; i < friends_list.size(); i++ ){
    cout << "in loop friend # " << i << endl;
    // get properties of entity
    result = do_request(methods::GET, basic_url
      + data_table_name + "/"
      + read_entity_admin + "/"
      + string(friends_list[i].first) + "/"
      + string(friends_list[i].second) );
    //get old updates

    old_updates = get_json_object_prop(result.second, "Updates");
    cout << "Old Statuses: " << old_updates << endl;

    //add new update
    new_updates = string(paths[3]) + "\n" + old_updates;
    update_property.push_back( make_pair("Updates", value::string(new_updates) ) );
    //update property of friend
    result = do_request(methods::PUT, basic_url
      + data_table_name + "/"
      + update_entity_admin + "/"
      + string(friends_list[i].first) + "/"
      + string(friends_list[i].second), value::object(update_property) );

    update_property.clear();
    cout << "Updated Statuses: " << new_updates << endl;
  }

  message.reply(status_codes::OK);
  return;
}

int main (int argc, char const * argv[]) {
  cout << "Parsing connection string" << endl;
  //table_cache.init (storage_connection_string);

  cout << "Opening listener" << endl;
  http_listener listener {def_url};
  listener.support(methods::POST, &handle_post); // Push a status update to friends
  listener.open().wait();

  cout << "Enter carriage return to stop server." << endl;
  string line;
  getline(std::cin, line);

  // Shut it down
  listener.close().wait();
  cout << "Closed" << endl;
}
<commit_msg>update Push Server<commit_after>/*
  Push Server code for CMPT 276 Group Assignment, Spring 2016.

  This server pushes status updates to all a user’s friends in the network.

  This server supports a single operation: push a status update to all friends
  of this user.

  This server handles disallowed method malformed request.
*/

#include <exception>
#include <iostream>
#include <memory>
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>
#include <string>

#include <cpprest/base_uri.h>
#include <cpprest/http_listener.h>
#include <cpprest/json.h>

#include <pplx/pplxtasks.h>

#include <was/common.h>
#include <was/storage_account.h>
#include <was/table.h>

#include "../include/TableCache.h"

#include "../include/make_unique.h"

//#include "../include/azure_keys.h"

#include "../include/ServerUtils.h"
#include "../include/ClientUtils.h"

using azure::storage::cloud_storage_account;
using azure::storage::storage_credentials;
using azure::storage::storage_exception;
using azure::storage::cloud_table;
using azure::storage::cloud_table_client;
using azure::storage::edm_type;
using azure::storage::entity_property;
using azure::storage::table_entity;
using azure::storage::table_operation;
using azure::storage::table_query;
using azure::storage::table_query_iterator;
using azure::storage::table_result;

using pplx::extensibility::critical_section_t;
using pplx::extensibility::scoped_critical_section_t;

using std::cin;
using std::cout;
using std::endl;
using std::getline;
using std::make_pair;
using std::pair;
using std::string;
using std::unordered_map;
using std::vector;

using web::http::http_headers;
using web::http::http_request;
using web::http::methods;
using web::http::status_code;
using web::http::status_codes;
using web::http::uri;

using web::json::value;

using web::http::experimental::listener::http_listener;

//using prop_vals_t = vector<pair<string,value>>;
using friends_list_t = std::vector<std::pair<std::string,std::string>>;

constexpr const char* basic_url = "http://localhost:34568/";
constexpr const char* def_url = "http://localhost:34574/";

const string push_status_op {"PushStatus"};
const string read_entity_admin {"ReadEntityAdmin"};
const string update_entity_admin {"UpdateEntityAdmin"};

const string data_table_name {"DataTable"};

//TableCache table_cache {};

//---------------------------------------------------------------------------------------

/*
  Return true if an HTTP request has a JSON body

  This routine can be called multiple times on the same message.
 */
bool has_json_body (http_request message) {
  return message.headers()["Content-type"] == "application/json";
}

/*
  Given an HTTP message with a JSON body, return the JSON
  body as an unordered map of strings to strings.
  get_json_body and get_json_bourne are valid and identical function calls.

  If the message has no JSON body, return an empty map.

  THIS ROUTINE CAN ONLY BE CALLED ONCE FOR A GIVEN MESSAGE
  (see http://microsoft.github.io/cpprestsdk/classweb_1_1http_1_1http__request.html#ae6c3d7532fe943de75dcc0445456cbc7
  for source of this limit).

  Note that all types of JSON values are returned as strings.
  Use C++ conversion utilities to convert to numbers or dates
  as necessary.
 */
unordered_map<string,string> get_json_body(http_request message) {
  unordered_map<string,string> results {};
  const http_headers& headers {message.headers()};
  auto content_type (headers.find("Content-Type"));
  if (content_type == headers.end() ||
      content_type->second != "application/json")
    return results;

  value json{};
  message.extract_json(true)
    .then([&json](value v) -> bool
          {
            json = v;
            return true;
          })
    .wait();

  if (json.is_object()) {
    for (const auto& v : json.as_object()) {
      if (v.second.is_string()) {
        results[v.first] = v.second.as_string();
      }
      else {
        results[v.first] = v.second.serialize();
      }
    }
  }
  return results;
}

unordered_map<string,string> get_json_bourne(http_request message) {
 return get_json_body(message);
}

//---------------------------------------------------------------------------------------

void handle_post (http_request message) {
  //TODO Not implemented yet!
  //message.reply(status_codes::NotImplemented);

  string path {uri::decode(message.relative_uri().path())};
  cout << endl << "**** POST " << path << endl;
  auto paths = uri::split_path(path);

  // need operation name, country, username, status = 4
  if (paths.size() != 4 ) {
    message.reply(status_codes::BadRequest);
    return;
  }
  //no PushStatus included
  else if(paths[0] != push_status_op) {
    message.reply(status_codes::BadRequest);
    return;
  }
  // no friends body included
  else if(!has_json_body(message)) {
    message.reply(status_codes::BadRequest);
    return;
  }
  unordered_map<string, string> json_body {get_json_bourne(message)};
  unordered_map<string, string>::const_iterator json_body_friends_iterator
    {json_body.find("Friends")};
  //no properties or more than one property
  if(json_body.size() != 1) {
    message.reply(status_codes::BadRequest);
    return;
  }
  // No 'Friends' property
  else if(json_body_friends_iterator == json_body.end()) {
    message.reply(status_codes::BadRequest);
    return;
  }

  if ( json_body_friends_iterator->second.empty() ){
    message.reply(status_codes::BadRequest);
    return;
  }
  vector<pair<string,string>> friends_list { parse_friends_list(json_body_friends_iterator->second) };
  string old_updates;
  string new_updates;
  pair<status_code,value> result;
  vector<pair<string,value>> update_property;

  for ( int i = 0; i < friends_list.size(); i++ ){
    cout << "in loop friend # " << i << endl;
    // get properties of entity
    result = do_request(methods::GET, basic_url
      + read_entity_admin + "/"
      + data_table_name + "/"
      + string(friends_list[i].first) + "/"
      + string(friends_list[i].second) );

    //get old updates

    old_updates = get_json_object_prop(result.second, "Updates");
    cout << "Old Statuses: " << old_updates << endl;

    //add new update
    new_updates = string(paths[3]) + "\n" + old_updates;
    update_property.push_back( make_pair("Updates", value::string(new_updates) ) );
    //update property of friend
    result = do_request(methods::PUT, basic_url
      + update_entity_admin + "/"
      + data_table_name + "/"
      + string(friends_list[i].first) + "/"
      + string(friends_list[i].second), value::object(update_property) );

    update_property.clear();
    cout << "Updated Statuses: " << new_updates << endl;
  }

  message.reply(status_codes::OK);
  return;
}

int main (int argc, char const * argv[]) {
  cout << "Parsing connection string" << endl;
  //table_cache.init (storage_connection_string);

  cout << "Opening listener" << endl;
  http_listener listener {def_url};
  listener.support(methods::POST, &handle_post); // Push a status update to friends
  listener.open().wait();

  cout << "Enter carriage return to stop server." << endl;
  string line;
  getline(std::cin, line);

  // Shut it down
  listener.close().wait();
  cout << "Closed" << endl;
}
<|endoftext|>


<commit_before>#include <cstring>

#include "SampleAndControl.h"
#include "chprintf.h"

#include "MPU6050.h"
#include "RearWheel.h"
#include "YawRateController.h"

SampleAndControl::SampleAndControl()
  : tp_control(0), tp_write(0)
{
}

void SampleAndControl::controlThread()
{
  chRegSetThreadName("Control");
  FRESULT res;
  msg_t msg;

  res = f_open(&f_, filename_, FA_CREATE_ALWAYS | FA_WRITE);
  if (res != FR_OK) {
    chSysHalt(); while (1) {}   // couldn't properly open the file!
  }

  uint32_t i = 0;
  for (; i < NUMBER_OF_SAMPLES; ++i)
    clearSample(samples[i]);

  MPU6050 & imu = MPU6050::Instance();
  imu.Initialize(&I2CD2);

  RearWheel & rw = RearWheel::Instance();
  rw.Reset();
  rw.turnOn();
  YawRateController & yc = YawRateController::Instance();
  yc.Reset();
  yc.turnOn();

  // zero out wheel encoders and system timer
  STM32_TIM4->CNT = STM32_TIM5->CNT = STM32_TIM8->CNT = 0;
  bool data = false;                // flag to indicate we have data to write
  uint32_t write_errors = 0;
  //uint32_t rw_fault_count = 0;
  //uint32_t steer_fault_count = 0;

  systime_t time = chTimeNow();     // Initial time
  for (uint32_t i = 0; !chThdShouldTerminate(); ++i) {
    time += MS2ST(con::T_ms);       // Next deadline

    // Get a sample to populate
    Sample & s = samples[i % NUMBER_OF_SAMPLES];

    // Begin data collection
    sampleTimers(s);  // sample system time/encoder counts/PWM duty cycle
    imu.Acquire(s);   // sample rate gyro, accelerometer and temperature sensors
    // sampleStates(s);  // sample various system states
    state_ = sampleSystemState() | Sample::CollectionEnabled;

    s.RearWheelRate_sp = rw.RateCommanded();
    s.YawRate_sp = yc.RateCommanded();
    // End data collection

    // Begin control
    if (rw.isEnabled() & (i % con::RW_N == 0))
      rw.Update(s.SystemTime, s.RearWheelAngle);

    if (yc.isEnabled() & (i % con::YC_N == 0))
      yc.Update(s);
    // End control

    // Begin data logging
    if (data && (i % (NUMBER_OF_SAMPLES/2) == 0)) {
      msg_t buffer;
      if ((i / (NUMBER_OF_SAMPLES/2)) & 1) {  // i is an odd multiple NUMBER_OF_SAMPLES/2  (64, 192, 320, ...)
        buffer = reinterpret_cast<msg_t>(samples);   // log data in samples[0:63]
      } else {  // i is an even multiple of NUMBER_OF_SAMPLES/2 (0, 128, 256, ...)
        buffer = reinterpret_cast<msg_t>(samples + (NUMBER_OF_SAMPLES/2)); // log data in samples[63:127]
      }
      state_ |= Sample::FileSystemWriteTriggered;
      msg = chMsgSend(tp_write, buffer);
      if (static_cast<FRESULT>(msg) != FR_OK) {
        ++write_errors;
      }
    }
    // End data logging

    data = true;

    // Save system state;
    state_ &= ~(Sample::CollectionEnabled | Sample::FileSystemWriteTriggered);
    s.SystemState = state_;
    // Measure computation time
    s.ComputationTime = STM32_TIM5->CNT - s.SystemTime;
    // Go to sleep until next 5ms interval
    chThdSleepUntil(time);
  } // for i @ 200Hz

  // Clean up
  imu.DeInitialize();
  rw.turnOff();
  yc.turnOff();
  rw.Reset();
  yc.Reset();
  // End cleanup

  msg = chMsgSend(tp_write, 0); // send a message to end the write thread.
  if (static_cast<FRESULT>(msg) != FR_OK) {
    ++write_errors;
  }

  while (tp_write) {
    chThdYield();           // yield this time slot so write thread can finish
  }

  // Write remaing samples in partially filled buffer.
  {
    uint32_t j = i % NUMBER_OF_SAMPLES;   // j \in [0, NUMBER_OF_SAMPLES)
    uint8_t * b;
    UINT bytes;
    if (j > (NUMBER_OF_SAMPLES/2 - 1)) {  // j \in [NUMBER_OF_SAMPLES/2, NUMBER_OF_SAMPLES)
      b = reinterpret_cast<uint8_t *>(samples + NUMBER_OF_SAMPLES/2);
      j %= NUMBER_OF_SAMPLES/2;
    } else {
      b = reinterpret_cast<uint8_t *>(samples);
    }
    if (j) { // there are samples to write
      res = f_write(&f_, b, sizeof(Sample)*j, &bytes);
    }
    if (res != FR_OK)
      ++write_errors;
  }

  f_close(&f_);           // close the file
  for (int i = 0; i < 24; ++i) // clear the filename
    filename_[i] = 0;       // clear the filename

  chThdExit(write_errors);
}

// Caller: Shell thread
void SampleAndControl::shellcmd(BaseSequentialStream *chp, int argc, char *argv[] __attribute__((unused)))
{
  if (argc == 0) {         // Start/Stop data collection, default filename
    if (tp_control) {      // Data collection enabled
      msg_t m = Stop();    // Stop it
      chprintf(chp, "Data collection and control terminated with %d errors.\r\n", m);
    } else {               // Data collecton disabled
      msg_t m = Start("samples.dat");// Start data collection to default file "samples.dat"
      if (m == 0) {
        chprintf(chp, "Data collection and control initiated.\r\n");
      } else {
        chprintf(chp, "Errors starting threads with error:  %d.\r\n", m);
      }
    }
  } else if (argc == 1) { // Start/Stop data collection, with filename
    if (tp_control) {     // Data collection enabled
      msg_t m = Stop();   // Stop it, ignoring the argument
      chprintf(chp, "Errors starting threads with error:  %d.\r\n", m);
    } else {              // Data collection is disabled
      msg_t m = Start(argv[0]);// Start data collection to file in argv[0]
      if (m == 0) {
        chprintf(chp, "Data collection and control initiated.\r\n");
      } else {
        chprintf(chp, "Errors starting threads with error:  %d.\r\n", m);
      }
    }
  } else {
    chprintf(chp, "Invalid usage.\r\n");
  }
}

//void SampleAndControl::setFilename(const char * name)
//{
//  std::strcpy(Filename_, name);
//}

//    Sample & s = sb.CurrentSample();
//    uint32_t state = 0;
//
//    s.SystemTime = STM32_TIM5->CNT;

    // Get rear wheel angle and rear wheel rotation direction
    // s.RearWheelAngle = rw.QuadratureCount();
    // state |= rw.RotationDir() | rw.CurrentDir();

//    s.FrontWheelAngle = STM32_TIM4->CNT;
//    s.SteerAngle = STM32_TIM3->CNT;
//
//    s.RearWheelRate_sp = rw.RateCommanded();
    // s.YawRate_sp = yc.RateCommanded();

    // Compute new speed control action if controller is enabled.
    // if (rw.isEnabled()) {
//      state |= Sample::RearWheelMotorEnable;
//      if (i % con::RW_N == 0) {    // only update control law every RW_N times
//        rw.Update(s.SystemTime, s.RearWheelAngle);
//      }
//     }

    // Compute new steer control action if yaw controller is enabled.
    //if (yc.isEnabled()) {
    //  state |= Sample::YawRateControl;
    //  yc.Update(s);
    //}

//    s.CCR_rw = rw.PWM_CCR();
    //s.CCR_steer = yc.PWM_CCR();

    // Check for motor faults
//    if (rw.hasFault()) {
//      state |= Sample::HubMotorFault;
//      ++rw_fault_count;
//      if (rw_fault_count > 10)  // debounce
//        break;
//    } else {
//      rw_fault_count = 0;
//    }
//
//    if (yc.hasFault()) {
//      state |= Sample::SteerMotorFault;
//      ++steer_fault_count;
//      if (steer_fault_count > 10)  // debounce
//        break;
//    } else {
//      steer_fault_count = 0;
//    }

//    s.SystemState = state;
  // sb.Reset();  // Throw away the data in the partially filled front buffer
  // rw.turnOff();
  // yc.turnOff();

void SampleAndControl::writeThread()
{
  UINT bytes;
  msg_t m = -1;
  FRESULT res = FR_OK;
  uint8_t *b;

  while (1) {
    Thread * calling_thread = chMsgWait();
    m = chMsgGet(calling_thread);
    chMsgRelease(calling_thread, res);

    if (m == 0) break;

    b = reinterpret_cast<uint8_t *>(m);

    res = f_write(&f_, b, sizeof(Sample)*NUMBER_OF_SAMPLES/2, &bytes);
  }
  tp_write = 0;
  chThdExit(0);
}

msg_t SampleAndControl::Start(const char * filename)
{
  msg_t m = 0;
  std::strcpy(filename_, filename);   // save the filename
  tp_write = chThdCreateStatic(SampleAndControl::waWriteThread,
                          sizeof(waWriteThread),
                          NORMALPRIO,
                          reinterpret_cast<tfunc_t>(writeThread_),
                          0);
  if (!tp_write) {
    m = (1 << 0);
  }
  tp_control = chThdCreateStatic(SampleAndControl::waControlThread,
                          sizeof(waControlThread),
                          NORMALPRIO,
                          reinterpret_cast<tfunc_t>(controlThread_),
                          0);
  if (!tp_control) {
    m |= (1 << 1);
  }

  return m;
}

<commit_msg>Fixed misplaced index counter.<commit_after>#include <cstring>

#include "SampleAndControl.h"
#include "chprintf.h"

#include "MPU6050.h"
#include "RearWheel.h"
#include "YawRateController.h"

SampleAndControl::SampleAndControl()
  : tp_control(0), tp_write(0), state_(0)
{
}

void SampleAndControl::controlThread()
{
  chRegSetThreadName("Control");
  FRESULT res;
  msg_t msg;

  res = f_open(&f_, filename_, FA_CREATE_ALWAYS | FA_WRITE);
  if (res != FR_OK) {
    chSysHalt(); while (1) {}   // couldn't properly open the file!
  }

  for (uint32_t i = 0; i < NUMBER_OF_SAMPLES; ++i)
    clearSample(samples[i]);

  MPU6050 & imu = MPU6050::Instance();
  imu.Initialize(&I2CD2);

  RearWheel & rw = RearWheel::Instance();
  rw.Reset();
  rw.turnOn();
  YawRateController & yc = YawRateController::Instance();
  yc.Reset();
  yc.turnOn();

  // zero out wheel encoders and system timer
  STM32_TIM4->CNT = STM32_TIM5->CNT = STM32_TIM8->CNT = 0;
  bool data = false;                // flag to indicate we have data to write
  uint32_t write_errors = 0;
  //uint32_t rw_fault_count = 0;
  //uint32_t steer_fault_count = 0;

  systime_t time = chTimeNow();     // Initial time
  uint32_t i;
  for (i = 0; !chThdShouldTerminate(); ++i) {
    time += MS2ST(con::T_ms);       // Next deadline

    // Get a sample to populate
    Sample & s = samples[i % NUMBER_OF_SAMPLES];

    // Begin data collection
    sampleTimers(s);  // sample system time/encoder counts/PWM duty cycle
    imu.Acquire(s);   // sample rate gyro, accelerometer and temperature sensors
    // sampleStates(s);  // sample various system states
    state_ = sampleSystemState() | Sample::CollectionEnabled;

    s.RearWheelRate_sp = rw.RateCommanded();
    s.YawRate_sp = yc.RateCommanded();
    // End data collection

    // Begin control
    if (rw.isEnabled() & (i % con::RW_N == 0))
      rw.Update(s.SystemTime, s.RearWheelAngle);

    if (yc.isEnabled() & (i % con::YC_N == 0))
      yc.Update(s);
    // End control

    // Begin data logging
    if (data && (i % (NUMBER_OF_SAMPLES/2) == 0)) {
      msg_t buffer;
      if ((i / (NUMBER_OF_SAMPLES/2)) & 1) {  // i is an odd multiple NUMBER_OF_SAMPLES/2  (64, 192, 320, ...)
        buffer = reinterpret_cast<msg_t>(samples);   // log data in samples[0:63]
      } else {  // i is an even multiple of NUMBER_OF_SAMPLES/2 (0, 128, 256, ...)
        buffer = reinterpret_cast<msg_t>(samples + (NUMBER_OF_SAMPLES/2)); // log data in samples[63:127]
      }
      state_ |= Sample::FileSystemWriteTriggered;
      msg = chMsgSend(tp_write, buffer);
      if (static_cast<FRESULT>(msg) != FR_OK) {
        ++write_errors;
      }
    }
    // End data logging

    data = true;

    // Save system state;
    state_ &= ~(Sample::CollectionEnabled | Sample::FileSystemWriteTriggered);
    s.SystemState = state_;
    // Measure computation time
    s.ComputationTime = STM32_TIM5->CNT - s.SystemTime;
    // Go to sleep until next 5ms interval
    chThdSleepUntil(time);
  } // for i @ 200Hz

  // Clean up
  imu.DeInitialize();
  rw.turnOff();
  yc.turnOff();
  rw.Reset();
  yc.Reset();
  // End cleanup

  msg = chMsgSend(tp_write, 0); // send a message to end the write thread.
  if (static_cast<FRESULT>(msg) != FR_OK) {
    ++write_errors;
  }

  while (tp_write) {
    chThdYield();           // yield this time slot so write thread can finish
  }

  // Write remaing samples in partially filled buffer.
  {
    uint32_t j = i % NUMBER_OF_SAMPLES;   // j \in [0, NUMBER_OF_SAMPLES)
    uint8_t * b;
    UINT bytes;
    if (j > (NUMBER_OF_SAMPLES/2 - 1)) {  // j \in [NUMBER_OF_SAMPLES/2, NUMBER_OF_SAMPLES)
      b = reinterpret_cast<uint8_t *>(samples + NUMBER_OF_SAMPLES/2);
      j %= NUMBER_OF_SAMPLES/2;
    } else {                              // j \in [0, NUMBER_OF_SAMPLES/2)
      b = reinterpret_cast<uint8_t *>(samples);
    }
    if (j) { // there are samples to write
      res = f_write(&f_, b, sizeof(Sample)*j, &bytes);
    }
    if (res != FR_OK)
      ++write_errors;
  }

  f_close(&f_);           // close the file
  for (int i = 0; i < 24; ++i) // clear the filename
    filename_[i] = 0;       // clear the filename

  chThdExit(write_errors);
}

// Caller: Shell thread
void SampleAndControl::shellcmd(BaseSequentialStream *chp, int argc, char *argv[] __attribute__((unused)))
{
  if (argc == 0) {         // Start/Stop data collection, default filename
    if (tp_control) {      // Data collection enabled
      msg_t m = Stop();    // Stop it
      chprintf(chp, "Data collection and control terminated with %d errors.\r\n", m);
    } else {               // Data collecton disabled
      msg_t m = Start("samples.dat");// Start data collection to default file "samples.dat"
      if (m == 0) {
        chprintf(chp, "Data collection and control initiated.\r\n");
      } else {
        chprintf(chp, "Errors starting threads with error:  %d.\r\n", m);
      }
    }
  } else if (argc == 1) { // Start/Stop data collection, with filename
    if (tp_control) {     // Data collection enabled
      msg_t m = Stop();   // Stop it, ignoring the argument
      chprintf(chp, "Errors starting threads with error:  %d.\r\n", m);
    } else {              // Data collection is disabled
      msg_t m = Start(argv[0]);// Start data collection to file in argv[0]
      if (m == 0) {
        chprintf(chp, "Data collection and control initiated.\r\n");
      } else {
        chprintf(chp, "Errors starting threads with error:  %d.\r\n", m);
      }
    }
  } else {
    chprintf(chp, "Invalid usage.\r\n");
  }
}

//void SampleAndControl::setFilename(const char * name)
//{
//  std::strcpy(Filename_, name);
//}

//    Sample & s = sb.CurrentSample();
//    uint32_t state = 0;
//
//    s.SystemTime = STM32_TIM5->CNT;

    // Get rear wheel angle and rear wheel rotation direction
    // s.RearWheelAngle = rw.QuadratureCount();
    // state |= rw.RotationDir() | rw.CurrentDir();

//    s.FrontWheelAngle = STM32_TIM4->CNT;
//    s.SteerAngle = STM32_TIM3->CNT;
//
//    s.RearWheelRate_sp = rw.RateCommanded();
    // s.YawRate_sp = yc.RateCommanded();

    // Compute new speed control action if controller is enabled.
    // if (rw.isEnabled()) {
//      state |= Sample::RearWheelMotorEnable;
//      if (i % con::RW_N == 0) {    // only update control law every RW_N times
//        rw.Update(s.SystemTime, s.RearWheelAngle);
//      }
//     }

    // Compute new steer control action if yaw controller is enabled.
    //if (yc.isEnabled()) {
    //  state |= Sample::YawRateControl;
    //  yc.Update(s);
    //}

//    s.CCR_rw = rw.PWM_CCR();
    //s.CCR_steer = yc.PWM_CCR();

    // Check for motor faults
//    if (rw.hasFault()) {
//      state |= Sample::HubMotorFault;
//      ++rw_fault_count;
//      if (rw_fault_count > 10)  // debounce
//        break;
//    } else {
//      rw_fault_count = 0;
//    }
//
//    if (yc.hasFault()) {
//      state |= Sample::SteerMotorFault;
//      ++steer_fault_count;
//      if (steer_fault_count > 10)  // debounce
//        break;
//    } else {
//      steer_fault_count = 0;
//    }

//    s.SystemState = state;
  // sb.Reset();  // Throw away the data in the partially filled front buffer
  // rw.turnOff();
  // yc.turnOff();

void SampleAndControl::writeThread()
{
  UINT bytes;
  msg_t m = -1;
  FRESULT res = FR_OK;
  uint8_t *b;

  while (1) {
    Thread * calling_thread = chMsgWait();
    m = chMsgGet(calling_thread);
    chMsgRelease(calling_thread, res);

    if (m == 0) break;

    b = reinterpret_cast<uint8_t *>(m);

    res = f_write(&f_, b, sizeof(Sample)*NUMBER_OF_SAMPLES/2, &bytes);
  }
  tp_write = 0;
  chThdExit(0);
}

msg_t SampleAndControl::Start(const char * filename)
{
  msg_t m = 0;
  std::strcpy(filename_, filename);   // save the filename
  tp_write = chThdCreateStatic(SampleAndControl::waWriteThread,
                          sizeof(waWriteThread),
                          NORMALPRIO,
                          reinterpret_cast<tfunc_t>(writeThread_),
                          0);
  if (!tp_write) {
    m = (1 << 0);
  }
  tp_control = chThdCreateStatic(SampleAndControl::waControlThread,
                          sizeof(waControlThread),
                          NORMALPRIO,
                          reinterpret_cast<tfunc_t>(controlThread_),
                          0);
  if (!tp_control) {
    m |= (1 << 1);
  }

  return m;
}

<|endoftext|>


<commit_before>#include "REventLoop.h"
#include "RThread.h"

static std::list<REventLoop *>
sThreadEventLoops;

REventLoop::REventLoop() : mReturnCode(0), mIsInterrupt(false)
{
}

REventLoop::~REventLoop()
{
  clear();
}

int
REventLoop::exec()
{
  while(true)
  {
    RThread::yieldCurrentThread();

    if(!processEvents())
    {
      clear();
      return mReturnCode;
    }

    // TODO: Only check timers on idle.
  }
}

void
REventLoop::exit(int returnCode)
{
  taskENTER_CRITICAL();
  mReturnCode  = returnCode;
  mIsInterrupt = true;
  taskEXIT_CRITICAL();
}

void
REventLoop::quit()
{
  exit(0);
}

bool
REventLoop::processEvents()
{
  bool result = true;

  while(!mEvents.empty())
  {
    auto eventData = mEvents.front();
    mEvents.pop_front();

    eventData.receiver->event(eventData.event);

    // Free the posted event
    delete eventData.event;

    if(mIsInterrupt)
    {
      result = false;
      goto LABEL_EXIT;
    }
  }

LABEL_EXIT:

  // TODO: Free all objects that marked as deleteLater

  return result;
}

REventLoop *
REventLoop::instance(const RThread *inThread)
{
  for(auto it = sThreadEventLoops.begin(); it != sThreadEventLoops.end(); ++it)
  {
    if(inThread == (*it)->thread())
    {
      return *it;
    }
  }

  // FIXME: Memory leaking without free!
  REventLoop *loop = new REventLoop();

  // FIXME: Thread event loops should be protected by mutexs.
  sThreadEventLoops.push_back(loop);

  return loop;
}

REventLoop *
REventLoop::instance()
{
  return instance(RThread::currentThread());
}

void
REventLoop::_destroy(const RThread *inThread)
{
  for(auto it = sThreadEventLoops.begin(); it != sThreadEventLoops.end(); ++it)
  {
    if(inThread == (*it)->thread())
    {
      sThreadEventLoops.erase(it);
      return;
    }
  }
}

void
REventLoop::postEvent(RObject *receiver, REvent *event)
{
  mEvents.push_back(EventData(receiver, event));
}

void
REventLoop::clear()
{
  for(auto it = mEvents.begin(); it != mEvents.end(); ++it)
  {
    delete it->event;
  }

  mEvents.clear();
}

bool
REventLoop::hasPendingEvents()
{
  return !mEvents.empty();
}
<commit_msg>Shorten static variant name "sThreadEventLoops" as "sEventLoops"<commit_after>#include "REventLoop.h"
#include "RThread.h"

static std::list<REventLoop *>
sEventLoops;

REventLoop::REventLoop() : mReturnCode(0), mIsInterrupt(false)
{
}

REventLoop::~REventLoop()
{
  clear();
}

int
REventLoop::exec()
{
  while(true)
  {
    RThread::yieldCurrentThread();

    if(!processEvents())
    {
      clear();
      return mReturnCode;
    }

    // TODO: Only check timers on idle.
  }
}

void
REventLoop::exit(int returnCode)
{
  taskENTER_CRITICAL();
  mReturnCode  = returnCode;
  mIsInterrupt = true;
  taskEXIT_CRITICAL();
}

void
REventLoop::quit()
{
  exit(0);
}

bool
REventLoop::processEvents()
{
  bool result = true;

  while(!mEvents.empty())
  {
    auto eventData = mEvents.front();
    mEvents.pop_front();

    eventData.receiver->event(eventData.event);

    // Free the posted event
    delete eventData.event;

    if(mIsInterrupt)
    {
      result = false;
      goto LABEL_EXIT;
    }
  }

LABEL_EXIT:

  // TODO: Free all objects that marked as deleteLater

  return result;
}

REventLoop *
REventLoop::instance(const RThread *inThread)
{
  for(auto it = sEventLoops.begin(); it != sEventLoops.end(); ++it)
  {
    if(inThread == (*it)->thread())
    {
      return *it;
    }
  }

  // FIXME: Memory leaking without free!
  REventLoop *loop = new REventLoop();

  // FIXME: Thread event loops should be protected by mutexs.
  sEventLoops.push_back(loop);

  return loop;
}

REventLoop *
REventLoop::instance()
{
  return instance(RThread::currentThread());
}

void
REventLoop::_destroy(const RThread *inThread)
{
  for(auto it = sEventLoops.begin(); it != sEventLoops.end(); ++it)
  {
    if(inThread == (*it)->thread())
    {
      sEventLoops.erase(it);
      return;
    }
  }
}

void
REventLoop::postEvent(RObject *receiver, REvent *event)
{
  mEvents.push_back(EventData(receiver, event));
}

void
REventLoop::clear()
{
  for(auto it = mEvents.begin(); it != mEvents.end(); ++it)
  {
    delete it->event;
  }

  mEvents.clear();
}

bool
REventLoop::hasPendingEvents()
{
  return !mEvents.empty();
}
<|endoftext|>


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

#include "ui/base/models/simple_menu_model.h"

#include "base/message_loop.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/base/l10n/l10n_util.h"

namespace ui {

const int kSeparatorId = -1;

// The instance is alive.
static const uint32 kMagicIdAlive = 0xCa11ab1e;

  // The instance has been deleted.
static const uint32 kMagicIdDead = 0xDECEA5ED;

struct SimpleMenuModel::Item {

// TODO(sky): Remove this when done investigating 95851.
#if defined(COMPILER_MSVC)
#pragma optimize("", off)
MSVC_PUSH_DISABLE_WARNING(4748)
#endif

  ~Item() {
    CHECK_EQ(magic_id, kMagicIdAlive);
    magic_id = kMagicIdDead;
  }

#if defined(COMPILER_MSVC)
MSVC_POP_WARNING()
#pragma optimize("", on)
#endif

  int command_id;
  string16 label;
  SkBitmap icon;
  ItemType type;
  int group_id;
  MenuModel* submenu;
  ButtonMenuItemModel* button_model;
  uint32 magic_id;
};

////////////////////////////////////////////////////////////////////////////////
// SimpleMenuModel::Delegate, public:

bool SimpleMenuModel::Delegate::IsCommandIdVisible(int command_id) const {
  return true;
}

bool SimpleMenuModel::Delegate::IsItemForCommandIdDynamic(
    int command_id) const {
  return false;
}

string16 SimpleMenuModel::Delegate::GetLabelForCommandId(int command_id) const {
  return string16();
}

bool SimpleMenuModel::Delegate::GetIconForCommandId(
    int command_id, SkBitmap* bitmap) const {
  return false;
}

void SimpleMenuModel::Delegate::CommandIdHighlighted(int command_id) {
}

void SimpleMenuModel::Delegate::ExecuteCommand(
    int command_id, int event_flags) {
  ExecuteCommand(command_id);
}

void SimpleMenuModel::Delegate::MenuWillShow(SimpleMenuModel* /*source*/) {
}

void SimpleMenuModel::Delegate::MenuClosed(SimpleMenuModel* /*source*/) {
}

////////////////////////////////////////////////////////////////////////////////
// SimpleMenuModel, public:

SimpleMenuModel::SimpleMenuModel(Delegate* delegate)
    : delegate_(delegate),
      menu_model_delegate_(NULL),
      ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) {
}

SimpleMenuModel::~SimpleMenuModel() {
  for (size_t i = 0; i < items_.size(); ++i)
    CHECK_EQ(kMagicIdAlive, items_[i].magic_id) << i;
}

void SimpleMenuModel::AddItem(int command_id, const string16& label) {
  Item item = { command_id, label, SkBitmap(), TYPE_COMMAND, -1, NULL, NULL,
                kMagicIdAlive};
  AppendItem(item);
}

void SimpleMenuModel::AddItemWithStringId(int command_id, int string_id) {
  AddItem(command_id, l10n_util::GetStringUTF16(string_id));
}

void SimpleMenuModel::AddSeparator() {
  Item item = { kSeparatorId, string16(), SkBitmap(), TYPE_SEPARATOR, -1,
                NULL, NULL, kMagicIdAlive };
  AppendItem(item);
}

void SimpleMenuModel::AddCheckItem(int command_id, const string16& label) {
  Item item = { command_id, label, SkBitmap(), TYPE_CHECK, -1, NULL,
                NULL, kMagicIdAlive };
  AppendItem(item);
}

void SimpleMenuModel::AddCheckItemWithStringId(int command_id, int string_id) {
  AddCheckItem(command_id, l10n_util::GetStringUTF16(string_id));
}

void SimpleMenuModel::AddRadioItem(int command_id, const string16& label,
                                   int group_id) {
  Item item = { command_id, label, SkBitmap(), TYPE_RADIO, group_id, NULL,
                NULL, kMagicIdAlive };
  AppendItem(item);
}

void SimpleMenuModel::AddRadioItemWithStringId(int command_id, int string_id,
                                               int group_id) {
  AddRadioItem(command_id, l10n_util::GetStringUTF16(string_id), group_id);
}

void SimpleMenuModel::AddButtonItem(int command_id,
                                    ButtonMenuItemModel* model) {
  Item item = { command_id, string16(), SkBitmap(), TYPE_BUTTON_ITEM, -1, NULL,
                model, kMagicIdAlive };
  AppendItem(item);
}

void SimpleMenuModel::AddSubMenu(int command_id, const string16& label,
                                 MenuModel* model) {
  Item item = { command_id, label, SkBitmap(), TYPE_SUBMENU, -1, model, NULL,
                kMagicIdAlive };
  AppendItem(item);
}

void SimpleMenuModel::AddSubMenuWithStringId(int command_id,
                                             int string_id, MenuModel* model) {
  AddSubMenu(command_id, l10n_util::GetStringUTF16(string_id), model);
}

void SimpleMenuModel::InsertItemAt(
    int index, int command_id, const string16& label) {
  Item item = { command_id, label, SkBitmap(), TYPE_COMMAND, -1, NULL, NULL,
                kMagicIdAlive };
  InsertItemAtIndex(item, index);
}

void SimpleMenuModel::InsertItemWithStringIdAt(
    int index, int command_id, int string_id) {
  InsertItemAt(index, command_id, l10n_util::GetStringUTF16(string_id));
}

void SimpleMenuModel::InsertSeparatorAt(int index) {
  Item item = { kSeparatorId, string16(), SkBitmap(), TYPE_SEPARATOR, -1,
                NULL, NULL, kMagicIdAlive };
  InsertItemAtIndex(item, index);
}

void SimpleMenuModel::InsertCheckItemAt(
    int index, int command_id, const string16& label) {
  Item item = { command_id, label, SkBitmap(), TYPE_CHECK, -1, NULL, NULL,
                kMagicIdAlive };
  InsertItemAtIndex(item, index);
}

void SimpleMenuModel::InsertCheckItemWithStringIdAt(
    int index, int command_id, int string_id) {
  InsertCheckItemAt(
      FlipIndex(index), command_id, l10n_util::GetStringUTF16(string_id));
}

void SimpleMenuModel::InsertRadioItemAt(
    int index, int command_id, const string16& label, int group_id) {
  Item item = { command_id, label, SkBitmap(), TYPE_RADIO, group_id, NULL,
                NULL, kMagicIdAlive };
  InsertItemAtIndex(item, index);
}

void SimpleMenuModel::InsertRadioItemWithStringIdAt(
    int index, int command_id, int string_id, int group_id) {
  InsertRadioItemAt(
      index, command_id, l10n_util::GetStringUTF16(string_id), group_id);
}

void SimpleMenuModel::InsertSubMenuAt(
    int index, int command_id, const string16& label, MenuModel* model) {
  Item item = { command_id, label, SkBitmap(), TYPE_SUBMENU, -1, model, NULL,
                kMagicIdAlive };
  InsertItemAtIndex(item, index);
}

void SimpleMenuModel::InsertSubMenuWithStringIdAt(
    int index, int command_id, int string_id, MenuModel* model) {
  InsertSubMenuAt(index, command_id, l10n_util::GetStringUTF16(string_id),
                  model);
}

void SimpleMenuModel::SetIcon(int index, const SkBitmap& icon) {
  items_[index].icon = icon;
}

void SimpleMenuModel::Clear() {
  items_.clear();
}

int SimpleMenuModel::GetIndexOfCommandId(int command_id) {
  std::vector<Item>::iterator itr;
  for (itr = items_.begin(); itr != items_.end(); itr++) {
    if ((*itr).command_id == command_id) {
      return FlipIndex(static_cast<int>(std::distance(items_.begin(), itr)));
    }
  }
  return -1;
}

////////////////////////////////////////////////////////////////////////////////
// SimpleMenuModel, MenuModel implementation:

bool SimpleMenuModel::HasIcons() const {
  for (std::vector<Item>::const_iterator iter = items_.begin();
       iter != items_.end(); ++iter) {
    if (!iter->icon.isNull())
      return true;
  }

  return false;
}

int SimpleMenuModel::GetItemCount() const {
  return static_cast<int>(items_.size());
}

MenuModel::ItemType SimpleMenuModel::GetTypeAt(int index) const {
  return items_.at(FlipIndex(index)).type;
}

int SimpleMenuModel::GetCommandIdAt(int index) const {
  return items_.at(FlipIndex(index)).command_id;
}

string16 SimpleMenuModel::GetLabelAt(int index) const {
  if (IsItemDynamicAt(index))
    return delegate_->GetLabelForCommandId(GetCommandIdAt(index));
  return items_.at(FlipIndex(index)).label;
}

bool SimpleMenuModel::IsItemDynamicAt(int index) const {
  if (delegate_)
    return delegate_->IsItemForCommandIdDynamic(GetCommandIdAt(index));
  return false;
}

bool SimpleMenuModel::GetAcceleratorAt(int index,
                                       ui::Accelerator* accelerator) const {
  if (delegate_) {
    return delegate_->GetAcceleratorForCommandId(GetCommandIdAt(index),
                                                 accelerator);
  }
  return false;
}

bool SimpleMenuModel::IsItemCheckedAt(int index) const {
  if (!delegate_)
    return false;
  int item_index = FlipIndex(index);
  MenuModel::ItemType item_type = items_[item_index].type;
  return (item_type == TYPE_CHECK || item_type == TYPE_RADIO) ?
      delegate_->IsCommandIdChecked(GetCommandIdAt(index)) : false;
}

int SimpleMenuModel::GetGroupIdAt(int index) const {
  return items_.at(FlipIndex(index)).group_id;
}

bool SimpleMenuModel::GetIconAt(int index, SkBitmap* icon) {
  if (IsItemDynamicAt(index))
    return delegate_->GetIconForCommandId(GetCommandIdAt(index), icon);

  if (items_[index].icon.isNull())
    return false;

  *icon = items_[index].icon;
  return true;
}

ButtonMenuItemModel* SimpleMenuModel::GetButtonMenuItemAt(int index) const {
  return items_.at(FlipIndex(index)).button_model;
}

bool SimpleMenuModel::IsEnabledAt(int index) const {
  int command_id = GetCommandIdAt(index);
  if (!delegate_ || command_id == kSeparatorId ||
      items_.at(FlipIndex(index)).button_model)
    return true;
  return delegate_->IsCommandIdEnabled(command_id);
}

bool SimpleMenuModel::IsVisibleAt(int index) const {
  int command_id = GetCommandIdAt(index);
  if (!delegate_ || command_id == kSeparatorId ||
      items_.at(FlipIndex(index)).button_model)
    return true;
  return delegate_->IsCommandIdVisible(command_id);
}

void SimpleMenuModel::HighlightChangedTo(int index) {
  if (delegate_)
    delegate_->CommandIdHighlighted(GetCommandIdAt(index));
}

void SimpleMenuModel::ActivatedAt(int index) {
  if (delegate_)
    delegate_->ExecuteCommand(GetCommandIdAt(index));
}

void SimpleMenuModel::ActivatedAt(int index, int event_flags) {
  if (delegate_)
    delegate_->ExecuteCommand(GetCommandIdAt(index), event_flags);
}

MenuModel* SimpleMenuModel::GetSubmenuModelAt(int index) const {
  return items_.at(FlipIndex(index)).submenu;
}

void SimpleMenuModel::MenuWillShow() {
  if (delegate_)
    delegate_->MenuWillShow(this);
}

void SimpleMenuModel::MenuClosed() {
  // Due to how menus work on the different platforms, ActivatedAt will be
  // called after this.  It's more convenient for the delegate to be called
  // afterwards though, so post a task.
  MessageLoop::current()->PostTask(
      FROM_HERE,
      method_factory_.NewRunnableMethod(&SimpleMenuModel::OnMenuClosed));
}

void SimpleMenuModel::SetMenuModelDelegate(
      ui::MenuModelDelegate* menu_model_delegate) {
  menu_model_delegate_ = menu_model_delegate;
}

void SimpleMenuModel::OnMenuClosed() {
  if (delegate_)
    delegate_->MenuClosed(this);
}

int SimpleMenuModel::FlipIndex(int index) const {
  return index;
}

////////////////////////////////////////////////////////////////////////////////
// SimpleMenuModel, Private:

void SimpleMenuModel::AppendItem(const Item& item) {
  ValidateItem(item);
  items_.push_back(item);
}

void SimpleMenuModel::InsertItemAtIndex(const Item& item, int index) {
  ValidateItem(item);
  items_.insert(items_.begin() + FlipIndex(index), item);
}

void SimpleMenuModel::ValidateItem(const Item& item) {
#ifndef NDEBUG
  if (item.type == TYPE_SEPARATOR) {
    DCHECK_EQ(item.command_id, kSeparatorId);
  } else {
    DCHECK_GE(item.command_id, 0);
  }
#endif  // NDEBUG
}

}  // namespace ui
<commit_msg>Reverts debugging code that was added to find a crasher. Crash went away.<commit_after>// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ui/base/models/simple_menu_model.h"

#include "base/message_loop.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/base/l10n/l10n_util.h"

namespace ui {

const int kSeparatorId = -1;

struct SimpleMenuModel::Item {
  int command_id;
  string16 label;
  SkBitmap icon;
  ItemType type;
  int group_id;
  MenuModel* submenu;
  ButtonMenuItemModel* button_model;
};

////////////////////////////////////////////////////////////////////////////////
// SimpleMenuModel::Delegate, public:

bool SimpleMenuModel::Delegate::IsCommandIdVisible(int command_id) const {
  return true;
}

bool SimpleMenuModel::Delegate::IsItemForCommandIdDynamic(
    int command_id) const {
  return false;
}

string16 SimpleMenuModel::Delegate::GetLabelForCommandId(int command_id) const {
  return string16();
}

bool SimpleMenuModel::Delegate::GetIconForCommandId(
    int command_id, SkBitmap* bitmap) const {
  return false;
}

void SimpleMenuModel::Delegate::CommandIdHighlighted(int command_id) {
}

void SimpleMenuModel::Delegate::ExecuteCommand(
    int command_id, int event_flags) {
  ExecuteCommand(command_id);
}

void SimpleMenuModel::Delegate::MenuWillShow(SimpleMenuModel* /*source*/) {
}

void SimpleMenuModel::Delegate::MenuClosed(SimpleMenuModel* /*source*/) {
}

////////////////////////////////////////////////////////////////////////////////
// SimpleMenuModel, public:

SimpleMenuModel::SimpleMenuModel(Delegate* delegate)
    : delegate_(delegate),
      menu_model_delegate_(NULL),
      ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) {
}

SimpleMenuModel::~SimpleMenuModel() {
}

void SimpleMenuModel::AddItem(int command_id, const string16& label) {
  Item item = { command_id, label, SkBitmap(), TYPE_COMMAND, -1, NULL, NULL };
  AppendItem(item);
}

void SimpleMenuModel::AddItemWithStringId(int command_id, int string_id) {
  AddItem(command_id, l10n_util::GetStringUTF16(string_id));
}

void SimpleMenuModel::AddSeparator() {
  Item item = { kSeparatorId, string16(), SkBitmap(), TYPE_SEPARATOR, -1,
                NULL, NULL };
  AppendItem(item);
}

void SimpleMenuModel::AddCheckItem(int command_id, const string16& label) {
  Item item = { command_id, label, SkBitmap(), TYPE_CHECK, -1, NULL, NULL };
  AppendItem(item);
}

void SimpleMenuModel::AddCheckItemWithStringId(int command_id, int string_id) {
  AddCheckItem(command_id, l10n_util::GetStringUTF16(string_id));
}

void SimpleMenuModel::AddRadioItem(int command_id, const string16& label,
                                   int group_id) {
  Item item = { command_id, label, SkBitmap(), TYPE_RADIO, group_id, NULL,
                NULL };
  AppendItem(item);
}

void SimpleMenuModel::AddRadioItemWithStringId(int command_id, int string_id,
                                               int group_id) {
  AddRadioItem(command_id, l10n_util::GetStringUTF16(string_id), group_id);
}

void SimpleMenuModel::AddButtonItem(int command_id,
                                    ButtonMenuItemModel* model) {
  Item item = { command_id, string16(), SkBitmap(), TYPE_BUTTON_ITEM, -1, NULL,
                model };
  AppendItem(item);
}

void SimpleMenuModel::AddSubMenu(int command_id, const string16& label,
                                 MenuModel* model) {
  Item item = { command_id, label, SkBitmap(), TYPE_SUBMENU, -1, model, NULL };
  AppendItem(item);
}

void SimpleMenuModel::AddSubMenuWithStringId(int command_id,
                                             int string_id, MenuModel* model) {
  AddSubMenu(command_id, l10n_util::GetStringUTF16(string_id), model);
}

void SimpleMenuModel::InsertItemAt(
    int index, int command_id, const string16& label) {
  Item item = { command_id, label, SkBitmap(), TYPE_COMMAND, -1, NULL, NULL };
  InsertItemAtIndex(item, index);
}

void SimpleMenuModel::InsertItemWithStringIdAt(
    int index, int command_id, int string_id) {
  InsertItemAt(index, command_id, l10n_util::GetStringUTF16(string_id));
}

void SimpleMenuModel::InsertSeparatorAt(int index) {
  Item item = { kSeparatorId, string16(), SkBitmap(), TYPE_SEPARATOR, -1,
                NULL, NULL };
  InsertItemAtIndex(item, index);
}

void SimpleMenuModel::InsertCheckItemAt(
    int index, int command_id, const string16& label) {
  Item item = { command_id, label, SkBitmap(), TYPE_CHECK, -1, NULL, NULL };
  InsertItemAtIndex(item, index);
}

void SimpleMenuModel::InsertCheckItemWithStringIdAt(
    int index, int command_id, int string_id) {
  InsertCheckItemAt(
      FlipIndex(index), command_id, l10n_util::GetStringUTF16(string_id));
}

void SimpleMenuModel::InsertRadioItemAt(
    int index, int command_id, const string16& label, int group_id) {
  Item item = { command_id, label, SkBitmap(), TYPE_RADIO, group_id, NULL,
                NULL };
  InsertItemAtIndex(item, index);
}

void SimpleMenuModel::InsertRadioItemWithStringIdAt(
    int index, int command_id, int string_id, int group_id) {
  InsertRadioItemAt(
      index, command_id, l10n_util::GetStringUTF16(string_id), group_id);
}

void SimpleMenuModel::InsertSubMenuAt(
    int index, int command_id, const string16& label, MenuModel* model) {
  Item item = { command_id, label, SkBitmap(), TYPE_SUBMENU, -1, model, NULL };
  InsertItemAtIndex(item, index);
}

void SimpleMenuModel::InsertSubMenuWithStringIdAt(
    int index, int command_id, int string_id, MenuModel* model) {
  InsertSubMenuAt(index, command_id, l10n_util::GetStringUTF16(string_id),
                  model);
}

void SimpleMenuModel::SetIcon(int index, const SkBitmap& icon) {
  items_[index].icon = icon;
}

void SimpleMenuModel::Clear() {
  items_.clear();
}

int SimpleMenuModel::GetIndexOfCommandId(int command_id) {
  std::vector<Item>::iterator itr;
  for (itr = items_.begin(); itr != items_.end(); itr++) {
    if ((*itr).command_id == command_id) {
      return FlipIndex(static_cast<int>(std::distance(items_.begin(), itr)));
    }
  }
  return -1;
}

////////////////////////////////////////////////////////////////////////////////
// SimpleMenuModel, MenuModel implementation:

bool SimpleMenuModel::HasIcons() const {
  for (std::vector<Item>::const_iterator iter = items_.begin();
       iter != items_.end(); ++iter) {
    if (!iter->icon.isNull())
      return true;
  }

  return false;
}

int SimpleMenuModel::GetItemCount() const {
  return static_cast<int>(items_.size());
}

MenuModel::ItemType SimpleMenuModel::GetTypeAt(int index) const {
  return items_.at(FlipIndex(index)).type;
}

int SimpleMenuModel::GetCommandIdAt(int index) const {
  return items_.at(FlipIndex(index)).command_id;
}

string16 SimpleMenuModel::GetLabelAt(int index) const {
  if (IsItemDynamicAt(index))
    return delegate_->GetLabelForCommandId(GetCommandIdAt(index));
  return items_.at(FlipIndex(index)).label;
}

bool SimpleMenuModel::IsItemDynamicAt(int index) const {
  if (delegate_)
    return delegate_->IsItemForCommandIdDynamic(GetCommandIdAt(index));
  return false;
}

bool SimpleMenuModel::GetAcceleratorAt(int index,
                                       ui::Accelerator* accelerator) const {
  if (delegate_) {
    return delegate_->GetAcceleratorForCommandId(GetCommandIdAt(index),
                                                 accelerator);
  }
  return false;
}

bool SimpleMenuModel::IsItemCheckedAt(int index) const {
  if (!delegate_)
    return false;
  int item_index = FlipIndex(index);
  MenuModel::ItemType item_type = items_[item_index].type;
  return (item_type == TYPE_CHECK || item_type == TYPE_RADIO) ?
      delegate_->IsCommandIdChecked(GetCommandIdAt(index)) : false;
}

int SimpleMenuModel::GetGroupIdAt(int index) const {
  return items_.at(FlipIndex(index)).group_id;
}

bool SimpleMenuModel::GetIconAt(int index, SkBitmap* icon) {
  if (IsItemDynamicAt(index))
    return delegate_->GetIconForCommandId(GetCommandIdAt(index), icon);

  if (items_[index].icon.isNull())
    return false;

  *icon = items_[index].icon;
  return true;
}

ButtonMenuItemModel* SimpleMenuModel::GetButtonMenuItemAt(int index) const {
  return items_.at(FlipIndex(index)).button_model;
}

bool SimpleMenuModel::IsEnabledAt(int index) const {
  int command_id = GetCommandIdAt(index);
  if (!delegate_ || command_id == kSeparatorId ||
      items_.at(FlipIndex(index)).button_model)
    return true;
  return delegate_->IsCommandIdEnabled(command_id);
}

bool SimpleMenuModel::IsVisibleAt(int index) const {
  int command_id = GetCommandIdAt(index);
  if (!delegate_ || command_id == kSeparatorId ||
      items_.at(FlipIndex(index)).button_model)
    return true;
  return delegate_->IsCommandIdVisible(command_id);
}

void SimpleMenuModel::HighlightChangedTo(int index) {
  if (delegate_)
    delegate_->CommandIdHighlighted(GetCommandIdAt(index));
}

void SimpleMenuModel::ActivatedAt(int index) {
  if (delegate_)
    delegate_->ExecuteCommand(GetCommandIdAt(index));
}

void SimpleMenuModel::ActivatedAt(int index, int event_flags) {
  if (delegate_)
    delegate_->ExecuteCommand(GetCommandIdAt(index), event_flags);
}

MenuModel* SimpleMenuModel::GetSubmenuModelAt(int index) const {
  return items_.at(FlipIndex(index)).submenu;
}

void SimpleMenuModel::MenuWillShow() {
  if (delegate_)
    delegate_->MenuWillShow(this);
}

void SimpleMenuModel::MenuClosed() {
  // Due to how menus work on the different platforms, ActivatedAt will be
  // called after this.  It's more convenient for the delegate to be called
  // afterwards though, so post a task.
  MessageLoop::current()->PostTask(
      FROM_HERE,
      method_factory_.NewRunnableMethod(&SimpleMenuModel::OnMenuClosed));
}

void SimpleMenuModel::SetMenuModelDelegate(
      ui::MenuModelDelegate* menu_model_delegate) {
  menu_model_delegate_ = menu_model_delegate;
}

void SimpleMenuModel::OnMenuClosed() {
  if (delegate_)
    delegate_->MenuClosed(this);
}

int SimpleMenuModel::FlipIndex(int index) const {
  return index;
}

////////////////////////////////////////////////////////////////////////////////
// SimpleMenuModel, Private:

void SimpleMenuModel::AppendItem(const Item& item) {
  ValidateItem(item);
  items_.push_back(item);
}

void SimpleMenuModel::InsertItemAtIndex(const Item& item, int index) {
  ValidateItem(item);
  items_.insert(items_.begin() + FlipIndex(index), item);
}

void SimpleMenuModel::ValidateItem(const Item& item) {
#ifndef NDEBUG
  if (item.type == TYPE_SEPARATOR) {
    DCHECK_EQ(item.command_id, kSeparatorId);
  } else {
    DCHECK_GE(item.command_id, 0);
  }
#endif  // NDEBUG
}

}  // namespace ui
<|endoftext|>