<commit_before>// Begin CVS Header
//   $Source: /Volumes/Home/Users/shoops/cvs/copasi_dev/copasi/utilities/CAnnotatedMatrix.cpp,v $
//   $Revision: 1.25 $
//   $Name:  $
//   $Author: ssahle $
//   $Date: 2008/09/25 22:40:02 $
// End CVS Header

// Copyright (C) 2008 by Pedro Mendes, Virginia Tech Intellectual
// Properties, Inc., EML Research, gGmbH, University of Heidelberg,
// and The University of Manchester.
// All rights reserved.

// Copyright (C) 2001 - 2007 by Pedro Mendes, Virginia Tech Intellectual
// Properties, Inc. and EML Research, gGmbH.
// All rights reserved.

/*!
    \file CAnnotatedMatrix.cpp
    \brief Implementation file of class CCopasiArray and CArrayAnnotation
 */

#include <sstream>
#include "CAnnotatedMatrix.h"
#include "CCopasiVector.h"

#include "report/CKeyFactory.h"
#include "report/CArrayElementReference.h"

CArrayAnnotation::CArrayAnnotation(const std::string & name,
                                   const CCopasiContainer * pParent,
                                   CCopasiAbstractArray * array,
                                   const bool & adopt)
    : CCopasiContainer(name, pParent, "Array" , CCopasiObject::Array),
    mpArray(array),
    mDestructArray(adopt),
    mDefaultMode(OBJECTS)
{
  assert(mpArray);

  resize();

  //addObjectReference("Annotated Matrix", *this, CCopasiObject::ValueDbl);
}

CArrayAnnotation::~CArrayAnnotation()
{
  if (mDestructArray)
    pdelete(mpArray);
}

void CArrayAnnotation::setArray(CCopasiAbstractArray * a)
{
  mpArray = a;
}

void CArrayAnnotation::setMode(unsigned int d, Mode m)
{
  mModes[d] = m;
  resizeOneDimension(d);
}

void CArrayAnnotation::setMode(Mode m)
{
  mDefaultMode = m;

  unsigned int i;
  for (i = 0; i < mModes.size(); ++i)
    setMode(i, m);
}

unsigned int CArrayAnnotation::dimensionality() const
  {
    return mModes.size();
  }

void CArrayAnnotation::setCopasiVector(unsigned int d, const CCopasiContainer* v)
{
  assert(d < mCopasiVectors.size());
  assert((mModes[d] == VECTOR) || (mModes[d] == VECTOR_ON_THE_FLY));

  mCopasiVectors[d] = v;
  if (mModes[d] == VECTOR)
    createAnnotationsCNFromCopasiVector(d, mCopasiVectors[d]);
}

void CArrayAnnotation::setAnnotationCN(unsigned int d, unsigned int i, const std::string cn)
{
  assert(d < mAnnotationsCN.size());
  assert(mModes[d] == OBJECTS);

  resizeOneDimension(d);
  assert(i < mAnnotationsCN[d].size());

  mAnnotationsCN[d][i] = cn;
}

void CArrayAnnotation::setAnnotationString(unsigned int d, unsigned int i, const std::string s)
{
  assert(d < mAnnotationsString.size());
  assert(mModes[d] == STRINGS);

  resizeOneDimension(d);
  assert(i < mAnnotationsString[d].size());

  mAnnotationsString[d][i] = s;
}

const std::vector<CRegisteredObjectName> & CArrayAnnotation::getAnnotationsCN(unsigned int d) const
  {
    assert(d < mModes.size());
    assert(mModes[d] != STRINGS);
    assert(mModes[d] != NUMBERS);

    if (mModes[d] == VECTOR_ON_THE_FLY)
      createAnnotationsCNFromCopasiVector(d, mCopasiVectors[d]);

    return mAnnotationsCN[d];
  }

const std::vector<std::string> & CArrayAnnotation::getAnnotationsString(unsigned int d, bool display) const
  {
    assert(d < mModes.size());

    //generate CNs (if necessary)
    if (mModes[d] == VECTOR_ON_THE_FLY)
      createAnnotationsCNFromCopasiVector(d, mCopasiVectors[d]);

    //generate DisplayNames
    if ((mModes[d] == VECTOR) || (mModes[d] == VECTOR_ON_THE_FLY) || (mModes[d] == OBJECTS))
      {
        unsigned int i, imax = mAnnotationsCN[d].size();
        mAnnotationsString[d].resize(imax);
        for (i = 0; i < imax; ++i)
          {
            const CCopasiObject * obj = CCopasiContainer::ObjectFromName(mAnnotationsCN[d][i]);
            if (obj)
              mAnnotationsString[d][i] =
                display ? obj->getObjectDisplayName() : obj->getObjectName();
            else
              mAnnotationsString[d][i] = "???";
          }
      }

    //generate Numbers
    if (mModes[d] == NUMBERS)
      createNumbers(d);

    // if the mode is STRINGS do nothing, mAnnotationsString is supposed to contain
    // the correct strings

    return mAnnotationsString[d];
  }

const std::string & CArrayAnnotation::getDimensionDescription(unsigned int d) const
  {
    assert(d < mDimensionDescriptions.size());
    return mDimensionDescriptions[d];
  }

void CArrayAnnotation::setDimensionDescription(unsigned int d, const std::string & s)
{
  assert(d < mDimensionDescriptions.size());
  mDimensionDescriptions[d] = s;
}

const std::string & CArrayAnnotation::getDescription() const
  {return mDescription;}

void CArrayAnnotation::setDescription(const std::string & s)
{mDescription = s;}

bool CArrayAnnotation::createAnnotationsCNFromCopasiVector(unsigned int d,
    const CCopasiContainer* v) const
  {
    if (!v) return false;
    if (! (v->isVector() || v->isNameVector())) return false;
    if (d >= mpArray->dimensionality()) return false;

    //now we know we have a vector. A CCopasiVector[N/S], hopefully, so that the following cast is valid:
    const CCopasiVector< CCopasiObject > * pVector =
      reinterpret_cast<const CCopasiVector< CCopasiObject > * >(v);

    mAnnotationsCN[d].resize(pVector->size());
    //if (pVector->size() < mAnnotations[d].size()) return false;

    unsigned int i;
    for (i = 0; i < mAnnotationsCN[d].size(); ++i)
      {
        if (!(*pVector)[i])
          return false;
        else
          mAnnotationsCN[d][i] = (*pVector)[i]->getCN();
      }
    return true;
  }

//private
void CArrayAnnotation::createNumbers(unsigned int /*d*/) const
  {
    //TODO
  }

//private
void CArrayAnnotation::reDimensionalize(unsigned int d)
{
  mAnnotationsCN.resize(d);
  mAnnotationsString.resize(d);
  mDimensionDescriptions.resize(d);
  mCopasiVectors.resize(d);
  mModes.resize(d, mDefaultMode);
}

//private
void CArrayAnnotation::resizeOneDimension(unsigned int d)
{
  if (mModes[d] != VECTOR)
    {
      mAnnotationsCN[d].resize(mpArray->size()[d]);
      mAnnotationsString[d].resize(mpArray->size()[d]);
    }
}

void CArrayAnnotation::resize()
{
  assert(mpArray);
  reDimensionalize(mpArray->dimensionality());

  unsigned int i, imax = mpArray->dimensionality();
  for (i = 0; i < imax; ++i)
    resizeOneDimension(i);
}

const CCopasiObject* CArrayAnnotation::addElementReference(CCopasiAbstractArray::index_type index)
{
  //generate the index string
  std::string tmp;
  std::ostringstream indexString;
  unsigned C_INT32 ii = 0;
  for (ii = 0; ii < index.size(); ++ii)
    {
      indexString << "[" << index[ii] << "]";
    }

  return this->getObject(indexString.str());
}

const CCopasiObject* CArrayAnnotation::addElementReference(C_INT32 u, C_INT32 v)
{
  CCopasiAbstractArray::index_type index;
  index.push_back(u);
  index.push_back(v);
  return addElementReference(index);
}

const CCopasiObject * CArrayAnnotation::getObject(const CCopasiObjectName & cn) const
  {
    std::cout << "CArrayAnnotation::getObject() " << cn << std::endl;

    if (cn == "")
      {
        return this;
      }

    //if there are no indices there could still be other children. This can be handled
    //by the container base class
    if (cn.getElementName(0, false) == "") //no indices
      return this->CCopasiContainer::getObject(cn);

    //so now we know we have indices. So we check if the array element reference
    //exists, if not we create it. Then getObject() of the element reference is called
    //with the remainder TODO

    /*
        //first get the array indices. At the moment only numerical indices...
        C_INT32 ii = 0;
        CCopasiArray::index_type index;
        while (cn.getElementName(ii, false) != "")
          {
            index.push_back(cn.getElementIndex(ii));
            ++ii;
          }
    */
    //first get the index string
    std::string tmp;
    std::string indexString;
    C_INT32 ii = 0;
    while ((tmp = cn.getElementName(ii, false)) != "")
      {
        indexString += "[" + tmp + "]";
        ++ii;
      }

    const CCopasiObject* pObject = NULL; //this will contain the element reference

    //if the reference object already exists, its name will be identical to the index
    std::pair< objectMap::const_iterator, objectMap::const_iterator > range =
      mObjects.equal_range(indexString);
    objectMap::const_iterator it = range.first;
    while (it != range.second && it->second->getObjectType() != "ElementReference") ++it;

    if (it == range.second) //not found
      {
        //create new element reference
        pObject = new CArrayElementReference(cn, this);
      }
    else
      {
        pObject = it->second;
      }

    if (pObject)
      return pObject->getObject(cn.getRemainder());
    else
      return NULL;

    /*
    if (index.size() != dimensionality())  //wrong number of indices for this array
      return NULL;

    unsigned C_INT32 i;
    for (i = 0; i < dimensionality(); ++i)
      if (index[i] >= size()[i]) //out of range
        return NULL;
    */

    //TODO remove element children after resize
    //TODO check optimization expression widget...
  }

void CArrayAnnotation::print(std::ostream * ostream) const
{*ostream << *this;}

#define SPC(level) std::string(level, ' ')

void CArrayAnnotation::printRecursively(std::ostream & ostream, C_INT32 level,
                                        CCopasiAbstractArray::index_type & index,
                                        const std::vector<std::vector<std::string> > & display) const
  {
    unsigned C_INT32 indent = 2 * (dimensionality() - 1 - level);

    if (level == 0) //only vector
      {
        ostream << SPC(indent) << "Rows: " << getDimensionDescription(level) << "\n";

        unsigned C_INT32 imax = size()[0];
        for (index[0] = 0; index[0] < imax; ++index[0])
          ostream << SPC(indent) << display[0][index[0]] << "\t" << (*array())[index] << "\n";
      }
    else if (level == 1) //matrix
      {
        ostream << SPC(indent) << "Rows:    " << getDimensionDescription(level - 1) << "\n";
        ostream << SPC(indent) << "Columns: " << getDimensionDescription(level) << "\n";

        unsigned C_INT32 imax = size()[0];
        unsigned C_INT32 jmax = size()[1];
        ostream << SPC(indent);
        for (index[1] = 0; index[1] < jmax; ++index[1])
          ostream << "\t" << display[1][index[1]];
        ostream << "\n";
        for (index[0] = 0; index[0] < imax; ++index[0])
          {
            ostream << SPC(indent) << display[0][index[0]];
            for (index[1] = 0; index[1] < jmax; ++index[1])
              ostream << "\t" << (*array())[index];
            ostream << "\n";
          }
      }
    else //dimensionality > 2
      {
        unsigned C_INT32 i, imax = size()[level];
        for (i = 0; i < imax; ++i)
          {
            ostream << SPC(indent) << getDimensionDescription(level) << ": " << display[level][i] << "\n";
            index[level] = i;
            printRecursively(ostream, level - 1, index, display);
          }
      }
  }

#undef SPC

std::ostream &operator<<(std::ostream &os, const CArrayAnnotation & o)
{
  if (!o.array()) return os;

  //cache the display names
  std::vector<std::vector<std::string> > displaynames;
  unsigned C_INT32 i;
  for (i = 0; i < o.dimensionality(); ++i)
    displaynames.push_back(o.getAnnotationsString(i));

  os << o.getObjectName() << std::endl;
  os << o.getDescription() << std::endl;

  CCopasiAbstractArray::index_type arraysize = o.array()->size();
  if (o.dimensionality() == 0)
    {
      //only one scalar value
      os << (*o.array())[arraysize] << std::endl;
    }
  else
    {
      o.printRecursively(os, o.dimensionality() - 1, arraysize, displaynames);
    }

  return os;
}

// void CArrayAnnotation::printDebugLoop(std::ostream & out, CCopasiAbstractArray::index_type & index, unsigned int level) const
//   {
//     unsigned int i, imax = mArray->size()[level];
//     for (i = 0; i < imax; ++i)
//       {
//         index[level] = i;
//
//         if (level == dimensionality() - 1)
//           {
//             out << (*mArray)[index] << "  ";
//}
//         else
//           {
//             printDebugLoop(out, index, level + 1);
//             out << std::endl;
//}
//}
//}
//
// void CArrayAnnotation::printDebug(std::ostream & out) const
//   {
//     out << mDescription << std::endl;
//     out << "  Dimensionality: " << dimensionality() << std::endl;
//
//     unsigned int i, j;
//     for (i = 0; i < dimensionality(); ++i)
//       {
//         out << "   " << i << ": " << mDimensionDescriptions[i] << std::endl;
//
//         //std::vector<std::string> names = const_cast<CArrayAnnotation*>(this)->getAnnotationsDisplay(i);
//
//         for (j = 0; j < mAnnotations[i].size(); ++j)
//           out << "     " << mAnnotations[i][j] /*<< " :" << names[j]*/ << std::endl;
//
//         out << std::endl;
//}
//
//     CCopasiAbstractArray::index_type index; index.resize(dimensionality());
//     printDebugLoop(out, index, 0);
//}
<commit_msg>Removed debug output.<commit_after>// Begin CVS Header
//   $Source: /Volumes/Home/Users/shoops/cvs/copasi_dev/copasi/utilities/CAnnotatedMatrix.cpp,v $
//   $Revision: 1.26 $
//   $Name:  $
//   $Author: shoops $
//   $Date: 2008/10/07 16:22:29 $
// End CVS Header

// Copyright (C) 2008 by Pedro Mendes, Virginia Tech Intellectual
// Properties, Inc., EML Research, gGmbH, University of Heidelberg,
// and The University of Manchester.
// All rights reserved.

// Copyright (C) 2001 - 2007 by Pedro Mendes, Virginia Tech Intellectual
// Properties, Inc. and EML Research, gGmbH.
// All rights reserved.

/*!
    \file CAnnotatedMatrix.cpp
    \brief Implementation file of class CCopasiArray and CArrayAnnotation
 */

#include <sstream>
#include "CAnnotatedMatrix.h"
#include "CCopasiVector.h"

#include "report/CKeyFactory.h"
#include "report/CArrayElementReference.h"

CArrayAnnotation::CArrayAnnotation(const std::string & name,
                                   const CCopasiContainer * pParent,
                                   CCopasiAbstractArray * array,
                                   const bool & adopt)
    : CCopasiContainer(name, pParent, "Array" , CCopasiObject::Array),
    mpArray(array),
    mDestructArray(adopt),
    mDefaultMode(OBJECTS)
{
  assert(mpArray);

  resize();

  //addObjectReference("Annotated Matrix", *this, CCopasiObject::ValueDbl);
}

CArrayAnnotation::~CArrayAnnotation()
{
  if (mDestructArray)
    pdelete(mpArray);
}

void CArrayAnnotation::setArray(CCopasiAbstractArray * a)
{
  mpArray = a;
}

void CArrayAnnotation::setMode(unsigned int d, Mode m)
{
  mModes[d] = m;
  resizeOneDimension(d);
}

void CArrayAnnotation::setMode(Mode m)
{
  mDefaultMode = m;

  unsigned int i;
  for (i = 0; i < mModes.size(); ++i)
    setMode(i, m);
}

unsigned int CArrayAnnotation::dimensionality() const
  {
    return mModes.size();
  }

void CArrayAnnotation::setCopasiVector(unsigned int d, const CCopasiContainer* v)
{
  assert(d < mCopasiVectors.size());
  assert((mModes[d] == VECTOR) || (mModes[d] == VECTOR_ON_THE_FLY));

  mCopasiVectors[d] = v;
  if (mModes[d] == VECTOR)
    createAnnotationsCNFromCopasiVector(d, mCopasiVectors[d]);
}

void CArrayAnnotation::setAnnotationCN(unsigned int d, unsigned int i, const std::string cn)
{
  assert(d < mAnnotationsCN.size());
  assert(mModes[d] == OBJECTS);

  resizeOneDimension(d);
  assert(i < mAnnotationsCN[d].size());

  mAnnotationsCN[d][i] = cn;
}

void CArrayAnnotation::setAnnotationString(unsigned int d, unsigned int i, const std::string s)
{
  assert(d < mAnnotationsString.size());
  assert(mModes[d] == STRINGS);

  resizeOneDimension(d);
  assert(i < mAnnotationsString[d].size());

  mAnnotationsString[d][i] = s;
}

const std::vector<CRegisteredObjectName> & CArrayAnnotation::getAnnotationsCN(unsigned int d) const
  {
    assert(d < mModes.size());
    assert(mModes[d] != STRINGS);
    assert(mModes[d] != NUMBERS);

    if (mModes[d] == VECTOR_ON_THE_FLY)
      createAnnotationsCNFromCopasiVector(d, mCopasiVectors[d]);

    return mAnnotationsCN[d];
  }

const std::vector<std::string> & CArrayAnnotation::getAnnotationsString(unsigned int d, bool display) const
  {
    assert(d < mModes.size());

    //generate CNs (if necessary)
    if (mModes[d] == VECTOR_ON_THE_FLY)
      createAnnotationsCNFromCopasiVector(d, mCopasiVectors[d]);

    //generate DisplayNames
    if ((mModes[d] == VECTOR) || (mModes[d] == VECTOR_ON_THE_FLY) || (mModes[d] == OBJECTS))
      {
        unsigned int i, imax = mAnnotationsCN[d].size();
        mAnnotationsString[d].resize(imax);
        for (i = 0; i < imax; ++i)
          {
            const CCopasiObject * obj = CCopasiContainer::ObjectFromName(mAnnotationsCN[d][i]);
            if (obj)
              mAnnotationsString[d][i] =
                display ? obj->getObjectDisplayName() : obj->getObjectName();
            else
              mAnnotationsString[d][i] = "???";
          }
      }

    //generate Numbers
    if (mModes[d] == NUMBERS)
      createNumbers(d);

    // if the mode is STRINGS do nothing, mAnnotationsString is supposed to contain
    // the correct strings

    return mAnnotationsString[d];
  }

const std::string & CArrayAnnotation::getDimensionDescription(unsigned int d) const
  {
    assert(d < mDimensionDescriptions.size());
    return mDimensionDescriptions[d];
  }

void CArrayAnnotation::setDimensionDescription(unsigned int d, const std::string & s)
{
  assert(d < mDimensionDescriptions.size());
  mDimensionDescriptions[d] = s;
}

const std::string & CArrayAnnotation::getDescription() const
  {return mDescription;}

void CArrayAnnotation::setDescription(const std::string & s)
{mDescription = s;}

bool CArrayAnnotation::createAnnotationsCNFromCopasiVector(unsigned int d,
    const CCopasiContainer* v) const
  {
    if (!v) return false;
    if (! (v->isVector() || v->isNameVector())) return false;
    if (d >= mpArray->dimensionality()) return false;

    //now we know we have a vector. A CCopasiVector[N/S], hopefully, so that the following cast is valid:
    const CCopasiVector< CCopasiObject > * pVector =
      reinterpret_cast<const CCopasiVector< CCopasiObject > * >(v);

    mAnnotationsCN[d].resize(pVector->size());
    //if (pVector->size() < mAnnotations[d].size()) return false;

    unsigned int i;
    for (i = 0; i < mAnnotationsCN[d].size(); ++i)
      {
        if (!(*pVector)[i])
          return false;
        else
          mAnnotationsCN[d][i] = (*pVector)[i]->getCN();
      }
    return true;
  }

//private
void CArrayAnnotation::createNumbers(unsigned int /*d*/) const
  {
    //TODO
  }

//private
void CArrayAnnotation::reDimensionalize(unsigned int d)
{
  mAnnotationsCN.resize(d);
  mAnnotationsString.resize(d);
  mDimensionDescriptions.resize(d);
  mCopasiVectors.resize(d);
  mModes.resize(d, mDefaultMode);
}

//private
void CArrayAnnotation::resizeOneDimension(unsigned int d)
{
  if (mModes[d] != VECTOR)
    {
      mAnnotationsCN[d].resize(mpArray->size()[d]);
      mAnnotationsString[d].resize(mpArray->size()[d]);
    }
}

void CArrayAnnotation::resize()
{
  assert(mpArray);
  reDimensionalize(mpArray->dimensionality());

  unsigned int i, imax = mpArray->dimensionality();
  for (i = 0; i < imax; ++i)
    resizeOneDimension(i);
}

const CCopasiObject* CArrayAnnotation::addElementReference(CCopasiAbstractArray::index_type index)
{
  //generate the index string
  std::string tmp;
  std::ostringstream indexString;
  unsigned C_INT32 ii = 0;
  for (ii = 0; ii < index.size(); ++ii)
    {
      indexString << "[" << index[ii] << "]";
    }

  return this->getObject(indexString.str());
}

const CCopasiObject* CArrayAnnotation::addElementReference(C_INT32 u, C_INT32 v)
{
  CCopasiAbstractArray::index_type index;
  index.push_back(u);
  index.push_back(v);
  return addElementReference(index);
}

const CCopasiObject * CArrayAnnotation::getObject(const CCopasiObjectName & cn) const
  {
    if (cn == "")
      {
        return this;
      }

    //if there are no indices there could still be other children. This can be handled
    //by the container base class
    if (cn.getElementName(0, false) == "") //no indices
      return this->CCopasiContainer::getObject(cn);

    //so now we know we have indices. So we check if the array element reference
    //exists, if not we create it. Then getObject() of the element reference is called
    //with the remainder TODO

    /*
        //first get the array indices. At the moment only numerical indices...
        C_INT32 ii = 0;
        CCopasiArray::index_type index;
        while (cn.getElementName(ii, false) != "")
          {
            index.push_back(cn.getElementIndex(ii));
            ++ii;
          }
    */
    //first get the index string
    std::string tmp;
    std::string indexString;
    C_INT32 ii = 0;
    while ((tmp = cn.getElementName(ii, false)) != "")
      {
        indexString += "[" + tmp + "]";
        ++ii;
      }

    const CCopasiObject* pObject = NULL; //this will contain the element reference

    //if the reference object already exists, its name will be identical to the index
    std::pair< objectMap::const_iterator, objectMap::const_iterator > range =
      mObjects.equal_range(indexString);
    objectMap::const_iterator it = range.first;
    while (it != range.second && it->second->getObjectType() != "ElementReference") ++it;

    if (it == range.second) //not found
      {
        //create new element reference
        pObject = new CArrayElementReference(cn, this);
      }
    else
      {
        pObject = it->second;
      }

    if (pObject)
      return pObject->getObject(cn.getRemainder());
    else
      return NULL;

    /*
    if (index.size() != dimensionality())  //wrong number of indices for this array
      return NULL;

    unsigned C_INT32 i;
    for (i = 0; i < dimensionality(); ++i)
      if (index[i] >= size()[i]) //out of range
        return NULL;
    */

    //TODO remove element children after resize
    //TODO check optimization expression widget...
  }

void CArrayAnnotation::print(std::ostream * ostream) const
{*ostream << *this;}

#define SPC(level) std::string(level, ' ')

void CArrayAnnotation::printRecursively(std::ostream & ostream, C_INT32 level,
                                        CCopasiAbstractArray::index_type & index,
                                        const std::vector<std::vector<std::string> > & display) const
  {
    unsigned C_INT32 indent = 2 * (dimensionality() - 1 - level);

    if (level == 0) //only vector
      {
        ostream << SPC(indent) << "Rows: " << getDimensionDescription(level) << "\n";

        unsigned C_INT32 imax = size()[0];
        for (index[0] = 0; index[0] < imax; ++index[0])
          ostream << SPC(indent) << display[0][index[0]] << "\t" << (*array())[index] << "\n";
      }
    else if (level == 1) //matrix
      {
        ostream << SPC(indent) << "Rows:    " << getDimensionDescription(level - 1) << "\n";
        ostream << SPC(indent) << "Columns: " << getDimensionDescription(level) << "\n";

        unsigned C_INT32 imax = size()[0];
        unsigned C_INT32 jmax = size()[1];
        ostream << SPC(indent);
        for (index[1] = 0; index[1] < jmax; ++index[1])
          ostream << "\t" << display[1][index[1]];
        ostream << "\n";
        for (index[0] = 0; index[0] < imax; ++index[0])
          {
            ostream << SPC(indent) << display[0][index[0]];
            for (index[1] = 0; index[1] < jmax; ++index[1])
              ostream << "\t" << (*array())[index];
            ostream << "\n";
          }
      }
    else //dimensionality > 2
      {
        unsigned C_INT32 i, imax = size()[level];
        for (i = 0; i < imax; ++i)
          {
            ostream << SPC(indent) << getDimensionDescription(level) << ": " << display[level][i] << "\n";
            index[level] = i;
            printRecursively(ostream, level - 1, index, display);
          }
      }
  }

#undef SPC

std::ostream &operator<<(std::ostream &os, const CArrayAnnotation & o)
{
  if (!o.array()) return os;

  //cache the display names
  std::vector<std::vector<std::string> > displaynames;
  unsigned C_INT32 i;
  for (i = 0; i < o.dimensionality(); ++i)
    displaynames.push_back(o.getAnnotationsString(i));

  os << o.getObjectName() << std::endl;
  os << o.getDescription() << std::endl;

  CCopasiAbstractArray::index_type arraysize = o.array()->size();
  if (o.dimensionality() == 0)
    {
      //only one scalar value
      os << (*o.array())[arraysize] << std::endl;
    }
  else
    {
      o.printRecursively(os, o.dimensionality() - 1, arraysize, displaynames);
    }

  return os;
}

// void CArrayAnnotation::printDebugLoop(std::ostream & out, CCopasiAbstractArray::index_type & index, unsigned int level) const
//   {
//     unsigned int i, imax = mArray->size()[level];
//     for (i = 0; i < imax; ++i)
//       {
//         index[level] = i;
//
//         if (level == dimensionality() - 1)
//           {
//             out << (*mArray)[index] << "  ";
//}
//         else
//           {
//             printDebugLoop(out, index, level + 1);
//             out << std::endl;
//}
//}
//}
//
// void CArrayAnnotation::printDebug(std::ostream & out) const
//   {
//     out << mDescription << std::endl;
//     out << "  Dimensionality: " << dimensionality() << std::endl;
//
//     unsigned int i, j;
//     for (i = 0; i < dimensionality(); ++i)
//       {
//         out << "   " << i << ": " << mDimensionDescriptions[i] << std::endl;
//
//         //std::vector<std::string> names = const_cast<CArrayAnnotation*>(this)->getAnnotationsDisplay(i);
//
//         for (j = 0; j < mAnnotations[i].size(); ++j)
//           out << "     " << mAnnotations[i][j] /*<< " :" << names[j]*/ << std::endl;
//
//         out << std::endl;
//}
//
//     CCopasiAbstractArray::index_type index; index.resize(dimensionality());
//     printDebugLoop(out, index, 0);
//}
<|endoftext|>


<commit_before>// Terminal.cpp -- source file
/*
 *
 * Author: septimomend (Ivan Chapkailo)
 *
 * 30.06.2017
 *
 */

#include "stdafx.h"
#include "Terminal.h"

// TODO: add definitions
<commit_msg>some functions definitions added<commit_after>// Terminal.cpp -- source file
/*
 *
 * Author: septimomend (Ivan Chapkailo)
 *
 * 30.06.2017
 *
 */

#include "stdafx.h"
#include "Terminal.h"

Terminal::Terminal() // cstr
{
}

void Terminal::emergencyDestruction(const char* str)
{
  write(STDOUT_FILENO, "\x1b[2J", 4); // write clr code to file
  write(STDOUT_FILENO, "\x1b[H", 3);

  perror(str);
  exit(1);  // exit with error
}

void Terminal::rowModeOff()
{
  // the change will occur after all output written to STDIN_FILENO is transmitted,
  // and all input so far received but not read will be discarded before the change is made
  if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &configObj.baseTermiosObj) == -1)
    emergencyDestruction("tcsetattr");
}

void Terminal::rowModeOn()
{
  if (tcgetattr(STDIN_FILENO, &configObj.baseTermiosObj) == -1)
    emergencyDestruction("tcgetattr");
  atexit(rowModeOff);

  struct termios raw = configObj.baseTermiosObj;
  // setting flags
  //
  raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
  raw.c_oflag &= ~(OPOST); // Post-process output
  raw.c_cflag |= (CS8); // Character size 8 bits
  raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
  raw.c_cc[VMIN] = 0;
  raw.c_cc[VTIME] = 1;
}
<|endoftext|>


<commit_before>#include <core/stdafx.h>
#include <core/propertyBag/registryProperty.h>
#include <core/mapi/mapiFunctions.h>
#include <core/utility/strings.h>
#include <core/utility/registry.h>
#include <core/utility/error.h>
#include <core/interpret/proptags.h>
#include <core/property/parseProperty.h>
#include <core/smartview/SmartView.h>

namespace propertybag
{
	registryProperty::registryProperty(HKEY hKey, _In_ const std::wstring& name, DWORD dwType)
		: m_hKey(hKey), m_name(name), m_dwType(dwType)
	{
		m_secure = false;
		// Get our prop tag and determine if we're a secure prop
		if (name.size() == 8 || name.size() == 9)
		{
			ULONG num{};
			// If we're not a simple prop tag, perhaps we have a prefix
			auto str = name;
			if (strings::stripPrefix(str, L"S") && strings::tryWstringToUlong(num, str, 16, false))
			{
				m_secure = true;
				// abuse some macros to swap the order of the tag
				m_ulPropTag = PROP_TAG(PROP_ID(num), PROP_TYPE(num));
			}
			else if (strings::tryWstringToUlong(num, name, 16, false))
			{
				// abuse some macros to swap the order of the tag
				m_ulPropTag = PROP_TAG(PROP_ID(num), PROP_TYPE(num));
			}
		}
	}

	void registryProperty::ensureSPropValue()
	{
		if (m_prop.ulPropTag != 0) return;
		m_prop = {m_ulPropTag};
		m_canParseMAPI = false;

		if (m_dwType == REG_BINARY)
		{
			m_binVal = registry::ReadBinFromRegistry(m_hKey, m_name);
			if (m_ulPropTag)
			{
				m_canParseMAPI = true;
				switch (PROP_TYPE(m_ulPropTag))
				{
				case PT_CLSID:
					if (m_binVal.size() == 16)
					{
						m_prop.Value.lpguid = reinterpret_cast<LPGUID>(const_cast<LPBYTE>(m_binVal.data()));
					}
					break;
				case PT_SYSTIME:
					if (m_binVal.size() == 8)
					{
						m_prop.Value.ft = *reinterpret_cast<LPFILETIME>(const_cast<LPBYTE>(m_binVal.data()));
					}
					break;
				case PT_I8:
					if (m_binVal.size() == 8)
					{
						m_prop.Value.li.QuadPart = static_cast<LONGLONG>(*m_binVal.data());
					}
					break;
				case PT_LONG:
					if (m_binVal.size() == 4)
					{
						m_prop.Value.l = static_cast<DWORD>(*m_binVal.data());
					}
					break;
				case PT_BOOLEAN:
					if (m_binVal.size() == 2)
					{
						m_prop.Value.b = static_cast<WORD>(*m_binVal.data());
					}
					break;
				case PT_BINARY:
					m_prop.Value.bin.cb = m_binVal.size();
					m_prop.Value.bin.lpb = const_cast<LPBYTE>(m_binVal.data());
					break;
				case PT_UNICODE:
					if (m_secure)
					{
						m_prop.ulPropTag = PROP_TAG(PT_BINARY, PROP_ID(m_ulPropTag));
						m_prop.Value.bin.cb = m_binVal.size();
						m_prop.Value.bin.lpb = const_cast<LPBYTE>(m_binVal.data());
					}
					else
					{
						m_unicodeVal = m_binVal;
						m_unicodeVal.push_back(0); // Add some null terminators just in case
						m_unicodeVal.push_back(0);
						m_prop.Value.lpszW = reinterpret_cast<LPWSTR>(const_cast<LPBYTE>(m_unicodeVal.data()));
					}
					break;
				default:
					m_prop.ulPropTag = PROP_TAG(PT_BINARY, PROP_ID(m_ulPropTag));
					m_prop.Value.bin.cb = m_binVal.size();
					m_prop.Value.bin.lpb = const_cast<LPBYTE>(m_binVal.data());
					m_canParseMAPI = false;
					break;
				}
			}
			else
			{
				m_prop.ulPropTag = PROP_TAG(PT_BINARY, PROP_ID_NULL);
				m_prop.Value.bin.cb = m_binVal.size();
				m_prop.Value.bin.lpb = const_cast<LPBYTE>(m_binVal.data());
			}
		}
		else if (m_dwType == REG_DWORD)
		{
			m_dwVal = registry::ReadDWORDFromRegistry(m_hKey, m_name);
			m_prop.Value.l = m_dwVal;
			if (m_ulPropTag)
			{
				m_canParseMAPI = true;
			}
			else
			{
				m_prop.ulPropTag = PROP_TAG(PT_LONG, PROP_ID_NULL);
			}
		}
		else if (m_dwType == REG_SZ)
		{
			m_szVal = registry::ReadStringFromRegistry(m_hKey, m_name);
			if (!m_prop.ulPropTag) m_prop.ulPropTag = PROP_TAG(PT_UNICODE, PROP_ID_NULL);
			switch (PROP_TYPE(m_prop.ulPropTag))
			{
			case PT_UNICODE:
				m_prop.Value.lpszW = m_szVal.data();
				break;
			case PT_STRING8:
				m_ansiVal = strings::wstringTostring(m_szVal);
				m_prop.Value.lpszA = m_ansiVal.data();
				break;
			}
		}
	}

	void registryProperty::ensureModel()
	{
		if (m_model) return;
		ensureSPropValue();
		m_model = std::make_shared<model::mapiRowModel>();
		if (m_ulPropTag != 0)
		{
			m_model->ulPropTag(m_ulPropTag);
			const auto propTagNames = proptags::PropTagToPropName(m_ulPropTag, false);
			if (!propTagNames.bestGuess.empty())
			{
				m_model->name(propTagNames.bestGuess);
			}

			if (!propTagNames.otherMatches.empty())
			{
				m_model->otherName(propTagNames.otherMatches);
			}
		}
		else
		{
			m_model->name(m_name);
			m_model->otherName(strings::format(L"%d", m_dwType)); // Just shoving in model to see it
		}

		if (m_secure) m_model->name(m_model->name() + L" (secure)");

		if (m_dwType == REG_BINARY)
		{
			if (!m_canParseMAPI)
			{
				if (!m_model->ulPropTag()) m_model->ulPropTag(PROP_TAG(PT_BINARY, PROP_ID_NULL));
				m_model->value(strings::BinToHexString(m_binVal, true));
				m_model->altValue(strings::BinToTextString(m_binVal, true));
			}
		}
		else if (m_dwType == REG_DWORD)
		{
			if (!m_ulPropTag)
			{
				m_model->ulPropTag(PROP_TAG(PT_LONG, PROP_ID_NULL));
				m_model->value(strings::format(L"%d", m_dwVal));
				m_model->altValue(strings::format(L"0x%08X", m_dwVal));
			}
		}
		else if (m_dwType == REG_SZ)
		{
			if (!m_model->ulPropTag()) m_model->ulPropTag(PROP_TAG(PT_UNICODE, PROP_ID_NULL));
			m_model->value(m_szVal);
		}

		if (m_canParseMAPI)
		{
			std::wstring PropString;
			std::wstring AltPropString;
			property::parseProperty(&m_prop, &PropString, &AltPropString);
			m_model->value(PropString);
			m_model->altValue(AltPropString);

			const auto szSmartView =
				smartview::parsePropertySmartView(&m_prop, nullptr, nullptr, nullptr, false, false);
			if (!szSmartView.empty()) m_model->smartView(szSmartView);
		}

		// For debugging purposes right now
		m_model->namedPropName(strings::BinToHexString(m_binVal, true));
		m_model->namedPropGuid(strings::BinToTextString(m_binVal, true));
	}

	void registryProperty::set(_In_opt_ LPSPropValue newValue)
	{
		if (!newValue) return;

		if (m_dwType == REG_BINARY)
		{
			if (m_ulPropTag)
			{
				switch (PROP_TYPE(m_ulPropTag))
				{
				case PT_CLSID:
					registry::WriteBinToRegistry(m_hKey, m_name, 16, LPBYTE(newValue->Value.lpguid));
					break;
				case PT_SYSTIME:
					registry::WriteBinToRegistry(m_hKey, m_name, 8, LPBYTE(&newValue->Value.ft));
					break;
				case PT_I8:
					registry::WriteBinToRegistry(m_hKey, m_name, 8, LPBYTE(&newValue->Value.li.QuadPart));
					break;
				case PT_LONG:
					registry::WriteBinToRegistry(m_hKey, m_name, 4, LPBYTE(&newValue->Value.l));
					break;
				case PT_BOOLEAN:
					registry::WriteBinToRegistry(m_hKey, m_name, 2, LPBYTE(&newValue->Value.b));
					break;
				case PT_BINARY:
					registry::WriteBinToRegistry(m_hKey, m_name, newValue->Value.bin.cb, newValue->Value.bin.lpb);
					break;
				case PT_UNICODE:
					// Include null terminator
					registry::WriteBinToRegistry(
						m_hKey,
						m_name,
						(std::wstring(newValue->Value.lpszW).length() + 1) * sizeof(wchar_t),
						LPBYTE(newValue->Value.lpszW));
					break;
				}
			}
			else
			{
				m_prop.ulPropTag = PROP_TAG(PT_BINARY, PROP_ID_NULL);
				m_prop.Value.bin.cb = m_binVal.size();
				m_prop.Value.bin.lpb = const_cast<LPBYTE>(m_binVal.data());
			}
		}
		else if (m_dwType == REG_DWORD)
		{
			if (PROP_TYPE(newValue->ulPropTag) == PT_LONG)
			{
				registry::WriteDWORDToRegistry(m_hKey, m_name, newValue->Value.l);
			}
		}
		else if (m_dwType == REG_SZ)
		{
			switch (PROP_TYPE(newValue->ulPropTag))
			{
			case PT_UNICODE:
				registry::WriteStringToRegistry(m_hKey, m_name, newValue->Value.lpszW);
				break;
			case PT_STRING8:
				registry::WriteStringToRegistry(m_hKey, m_name, strings::stringTowstring(newValue->Value.lpszA));
				break;
			}
		}

		m_model = nullptr;
		m_prop = {};
		ensureModel();
	}
} // namespace propertybag<commit_msg>add comment<commit_after>#include <core/stdafx.h>
#include <core/propertyBag/registryProperty.h>
#include <core/mapi/mapiFunctions.h>
#include <core/utility/strings.h>
#include <core/utility/registry.h>
#include <core/utility/error.h>
#include <core/interpret/proptags.h>
#include <core/property/parseProperty.h>
#include <core/smartview/SmartView.h>

namespace propertybag
{
	registryProperty::registryProperty(HKEY hKey, _In_ const std::wstring& name, DWORD dwType)
		: m_hKey(hKey), m_name(name), m_dwType(dwType)
	{
		m_secure = false;
		// Get our prop tag and determine if we're a secure prop
		if (name.size() == 8 || name.size() == 9)
		{
			ULONG num{};
			// If we're not a simple prop tag, perhaps we have a prefix
			auto str = name;
			if (strings::stripPrefix(str, L"S") && strings::tryWstringToUlong(num, str, 16, false))
			{
				m_secure = true;
				// abuse some macros to swap the order of the tag
				m_ulPropTag = PROP_TAG(PROP_ID(num), PROP_TYPE(num));
			}
			else if (strings::tryWstringToUlong(num, name, 16, false))
			{
				// abuse some macros to swap the order of the tag
				m_ulPropTag = PROP_TAG(PROP_ID(num), PROP_TYPE(num));
			}
		}
	}

	void registryProperty::ensureSPropValue()
	{
		if (m_prop.ulPropTag != 0) return;
		m_prop = {m_ulPropTag};
		m_canParseMAPI = false;

		if (m_dwType == REG_BINARY)
		{
			m_binVal = registry::ReadBinFromRegistry(m_hKey, m_name);
			if (m_ulPropTag)
			{
				m_canParseMAPI = true;
				switch (PROP_TYPE(m_ulPropTag))
				{
				case PT_CLSID:
					if (m_binVal.size() == 16)
					{
						m_prop.Value.lpguid = reinterpret_cast<LPGUID>(const_cast<LPBYTE>(m_binVal.data()));
					}
					break;
				case PT_SYSTIME:
					if (m_binVal.size() == 8)
					{
						m_prop.Value.ft = *reinterpret_cast<LPFILETIME>(const_cast<LPBYTE>(m_binVal.data()));
					}
					break;
				case PT_I8:
					if (m_binVal.size() == 8)
					{
						m_prop.Value.li.QuadPart = static_cast<LONGLONG>(*m_binVal.data());
					}
					break;
				case PT_LONG:
					if (m_binVal.size() == 4)
					{
						m_prop.Value.l = static_cast<DWORD>(*m_binVal.data());
					}
					break;
				case PT_BOOLEAN:
					if (m_binVal.size() == 2)
					{
						m_prop.Value.b = static_cast<WORD>(*m_binVal.data());
					}
					break;
				case PT_BINARY:
					m_prop.Value.bin.cb = m_binVal.size();
					m_prop.Value.bin.lpb = const_cast<LPBYTE>(m_binVal.data());
					break;
				case PT_UNICODE:
					if (m_secure)
					{
						// TODO - not showing right prop type in UI
						m_prop.ulPropTag = PROP_TAG(PT_BINARY, PROP_ID(m_ulPropTag));
						m_prop.Value.bin.cb = m_binVal.size();
						m_prop.Value.bin.lpb = const_cast<LPBYTE>(m_binVal.data());
					}
					else
					{
						m_unicodeVal = m_binVal;
						m_unicodeVal.push_back(0); // Add some null terminators just in case
						m_unicodeVal.push_back(0);
						m_prop.Value.lpszW = reinterpret_cast<LPWSTR>(const_cast<LPBYTE>(m_unicodeVal.data()));
					}
					break;
				default:
					m_prop.ulPropTag = PROP_TAG(PT_BINARY, PROP_ID(m_ulPropTag));
					m_prop.Value.bin.cb = m_binVal.size();
					m_prop.Value.bin.lpb = const_cast<LPBYTE>(m_binVal.data());
					m_canParseMAPI = false;
					break;
				}
			}
			else
			{
				m_prop.ulPropTag = PROP_TAG(PT_BINARY, PROP_ID_NULL);
				m_prop.Value.bin.cb = m_binVal.size();
				m_prop.Value.bin.lpb = const_cast<LPBYTE>(m_binVal.data());
			}
		}
		else if (m_dwType == REG_DWORD)
		{
			m_dwVal = registry::ReadDWORDFromRegistry(m_hKey, m_name);
			m_prop.Value.l = m_dwVal;
			if (m_ulPropTag)
			{
				m_canParseMAPI = true;
			}
			else
			{
				m_prop.ulPropTag = PROP_TAG(PT_LONG, PROP_ID_NULL);
			}
		}
		else if (m_dwType == REG_SZ)
		{
			m_szVal = registry::ReadStringFromRegistry(m_hKey, m_name);
			if (!m_prop.ulPropTag) m_prop.ulPropTag = PROP_TAG(PT_UNICODE, PROP_ID_NULL);
			switch (PROP_TYPE(m_prop.ulPropTag))
			{
			case PT_UNICODE:
				m_prop.Value.lpszW = m_szVal.data();
				break;
			case PT_STRING8:
				m_ansiVal = strings::wstringTostring(m_szVal);
				m_prop.Value.lpszA = m_ansiVal.data();
				break;
			}
		}
	}

	void registryProperty::ensureModel()
	{
		if (m_model) return;
		ensureSPropValue();
		m_model = std::make_shared<model::mapiRowModel>();
		if (m_ulPropTag != 0)
		{
			m_model->ulPropTag(m_ulPropTag);
			const auto propTagNames = proptags::PropTagToPropName(m_ulPropTag, false);
			if (!propTagNames.bestGuess.empty())
			{
				m_model->name(propTagNames.bestGuess);
			}

			if (!propTagNames.otherMatches.empty())
			{
				m_model->otherName(propTagNames.otherMatches);
			}
		}
		else
		{
			m_model->name(m_name);
			m_model->otherName(strings::format(L"%d", m_dwType)); // Just shoving in model to see it
		}

		if (m_secure) m_model->name(m_model->name() + L" (secure)");

		if (m_dwType == REG_BINARY)
		{
			if (!m_canParseMAPI)
			{
				if (!m_model->ulPropTag()) m_model->ulPropTag(PROP_TAG(PT_BINARY, PROP_ID_NULL));
				m_model->value(strings::BinToHexString(m_binVal, true));
				m_model->altValue(strings::BinToTextString(m_binVal, true));
			}
		}
		else if (m_dwType == REG_DWORD)
		{
			if (!m_ulPropTag)
			{
				m_model->ulPropTag(PROP_TAG(PT_LONG, PROP_ID_NULL));
				m_model->value(strings::format(L"%d", m_dwVal));
				m_model->altValue(strings::format(L"0x%08X", m_dwVal));
			}
		}
		else if (m_dwType == REG_SZ)
		{
			if (!m_model->ulPropTag()) m_model->ulPropTag(PROP_TAG(PT_UNICODE, PROP_ID_NULL));
			m_model->value(m_szVal);
		}

		if (m_canParseMAPI)
		{
			std::wstring PropString;
			std::wstring AltPropString;
			property::parseProperty(&m_prop, &PropString, &AltPropString);
			m_model->value(PropString);
			m_model->altValue(AltPropString);

			const auto szSmartView =
				smartview::parsePropertySmartView(&m_prop, nullptr, nullptr, nullptr, false, false);
			if (!szSmartView.empty()) m_model->smartView(szSmartView);
		}

		// For debugging purposes right now
		m_model->namedPropName(strings::BinToHexString(m_binVal, true));
		m_model->namedPropGuid(strings::BinToTextString(m_binVal, true));
	}

	void registryProperty::set(_In_opt_ LPSPropValue newValue)
	{
		if (!newValue) return;

		if (m_dwType == REG_BINARY)
		{
			if (m_ulPropTag)
			{
				switch (PROP_TYPE(m_ulPropTag))
				{
				case PT_CLSID:
					registry::WriteBinToRegistry(m_hKey, m_name, 16, LPBYTE(newValue->Value.lpguid));
					break;
				case PT_SYSTIME:
					registry::WriteBinToRegistry(m_hKey, m_name, 8, LPBYTE(&newValue->Value.ft));
					break;
				case PT_I8:
					registry::WriteBinToRegistry(m_hKey, m_name, 8, LPBYTE(&newValue->Value.li.QuadPart));
					break;
				case PT_LONG:
					registry::WriteBinToRegistry(m_hKey, m_name, 4, LPBYTE(&newValue->Value.l));
					break;
				case PT_BOOLEAN:
					registry::WriteBinToRegistry(m_hKey, m_name, 2, LPBYTE(&newValue->Value.b));
					break;
				case PT_BINARY:
					registry::WriteBinToRegistry(m_hKey, m_name, newValue->Value.bin.cb, newValue->Value.bin.lpb);
					break;
				case PT_UNICODE:
					// Include null terminator
					registry::WriteBinToRegistry(
						m_hKey,
						m_name,
						(std::wstring(newValue->Value.lpszW).length() + 1) * sizeof(wchar_t),
						LPBYTE(newValue->Value.lpszW));
					break;
				}
			}
			else
			{
				m_prop.ulPropTag = PROP_TAG(PT_BINARY, PROP_ID_NULL);
				m_prop.Value.bin.cb = m_binVal.size();
				m_prop.Value.bin.lpb = const_cast<LPBYTE>(m_binVal.data());
			}
		}
		else if (m_dwType == REG_DWORD)
		{
			if (PROP_TYPE(newValue->ulPropTag) == PT_LONG)
			{
				registry::WriteDWORDToRegistry(m_hKey, m_name, newValue->Value.l);
			}
		}
		else if (m_dwType == REG_SZ)
		{
			switch (PROP_TYPE(newValue->ulPropTag))
			{
			case PT_UNICODE:
				registry::WriteStringToRegistry(m_hKey, m_name, newValue->Value.lpszW);
				break;
			case PT_STRING8:
				registry::WriteStringToRegistry(m_hKey, m_name, strings::stringTowstring(newValue->Value.lpszA));
				break;
			}
		}

		m_model = nullptr;
		m_prop = {};
		ensureModel();
	}
} // namespace propertybag<|endoftext|>


<commit_before>/*
 * Copyright(c) Sophist Solutions, Inc. 1990-2015.  All rights reserved
 */
#include    "../StroikaPreComp.h"

#if     qHas_Syslog
#include    <syslog.h>
#endif

#include    "../Characters/CString/Utilities.h"
#include    "../Characters/Format.h"
#include    "../Debug/Trace.h"
#include    "BlockingQueue.h"
#include    "Common.h"
#include    "Process.h"
#include    "Thread.h"
#include    "TimeOutException.h"

#include    "Logger.h"

using   namespace   Stroika::Foundation;
using   namespace   Stroika::Foundation::Configuration;
using   namespace   Stroika::Foundation::Execution;

using   Memory::Optional;
using   Time::DurationSecondsType;

// Comment this in to turn on aggressive noisy DbgTrace in this module
//#define   USE_NOISY_TRACE_IN_THIS_MODULE_       1

/*
 ********************************************************************************
 ******************************** Execution::Logger *****************************
 ********************************************************************************
 */
Logger  Logger::sThe_;

const EnumNames<Logger::Priority>   Logger::Stroika_Enum_Names(Priority)
{
    { Logger::Priority::eDebug, L"Debug" },
    { Logger::Priority::eInfo, L"Info" },
    { Logger::Priority::eNotice, L"Notice" },
    { Logger::Priority::eWarning, L"Warning" },
    { Logger::Priority::eError, L"Error" },
    { Logger::Priority::eCriticalError, L"CriticalError" },
    { Logger::Priority::eAlertError, L"AlertError" },
    { Logger::Priority::eEmergency, L"Emergency" },
};

namespace {
    BlockingQueue<pair<Logger::Priority, String>>   sOutMsgQ_;
    Execution::Thread                               sBookkeepingThread_;
    bool                                            sOutQMaybeNeedsFlush_ = true;       // sligt optimziation of not using buffering

    Synchronized<Memory::Optional<DurationSecondsType>>       sSuppressDuplicatesThreshold_;

    struct LastMsg_ {
        mutex                               fMutex_;     // mutex so we can update related variables together
        pair<Logger::Priority, String>      fLastMsgSent_;
        Time::DurationSecondsType           fLastSentAt = 0.0;
        unsigned int                        fRepeatCount_ = 0;
    };
    LastMsg_    sLastMsg_;
}

Logger::Logger ()
    : fAppender_ ()
    , fMinLogLevel_ (Priority::eInfo)
    , fBufferingEnabled_ (false)
{
}

void    Logger::SetAppender (const shared_ptr<IAppenderRep>& rep)
{
    fAppender_ = rep;
}

void    Logger::Log_ (Priority logLevel, const String& format, va_list argList)
{
    shared_ptr<IAppenderRep> tmp =   sThe_.fAppender_;   // avoid races and critical sections
    if (tmp.get () != nullptr) {
        auto p = pair<Logger::Priority, String> (logLevel, Characters::FormatV (format.c_str (), argList));
        if (sSuppressDuplicatesThreshold_->IsPresent ()) {
            auto    critSec { make_unique_lock (sLastMsg_.fMutex_) };
            if (p == sLastMsg_.fLastMsgSent_) {
                sLastMsg_.fRepeatCount_++;
                sLastMsg_.fLastSentAt = Time::GetTickCount ();
                return; // so will be handled later
            }
            else {
                if (sLastMsg_.fRepeatCount_ > 0) {
                    FlushDupsWarning_ ();
                }
                sLastMsg_.fLastMsgSent_ = p;
                sLastMsg_.fRepeatCount_ = 0;
                sLastMsg_.fLastSentAt = Time::GetTickCount ();
            }
        }
        if (GetBufferingEnabled ()) {
            sOutQMaybeNeedsFlush_ = true;
            sOutMsgQ_.AddTail (p);
        }
        else {
            if (sOutQMaybeNeedsFlush_) {
                FlushBuffer (); // in case recently disabled
            }
            tmp->Log (p.first, p.second);
        }
    }
}

void        Logger::SetBufferingEnabled (bool logBufferingEnabled)
{
#if     USE_NOISY_TRACE_IN_THIS_MODULE_
    Debug::TraceContextBumper ctx (SDKSTR ("Logger::SetBufferingEnabled"));
    DbgTrace (L"(logBufferingEnabled=%d)", logBufferingEnabled);
#endif
    sThe_.fBufferingEnabled_ = logBufferingEnabled;
    UpdateBookkeepingThread_ ();
}

void        Logger::FlushBuffer ()
{
    shared_ptr<IAppenderRep> tmp =   sThe_.fAppender_;   // avoid races and critical sections
    if (tmp != nullptr) {
        while (true) {
            Optional<pair<Logger::Priority, String>> p = sOutMsgQ_.RemoveHeadIfPossible ();
            if (p.IsPresent ()) {
                tmp->Log (p->first, p->second);
            }
            else {
                return; // no more entries
            }
        }
    }
    sOutQMaybeNeedsFlush_ = false;
}

Memory::Optional<Time::DurationSecondsType> Logger::GetSuppressDuplicates ()
{
    return Memory::Optional<Time::DurationSecondsType> (sSuppressDuplicatesThreshold_);
}

void    Logger::SetSuppressDuplicates (const Memory::Optional<DurationSecondsType>& suppressDuplicatesThreshold)
{
#if     USE_NOISY_TRACE_IN_THIS_MODULE_
    Debug::TraceContextBumper ctx (SDKSTR ("Logger::SetSuppressDuplicates"));
    DbgTrace (L"(suppressDuplicatesThreshold=%f)", suppressDuplicatesThreshold.Value (-1));
#endif
    Require (suppressDuplicatesThreshold.IsMissing () or * suppressDuplicatesThreshold > 0.0);
    auto    critSec { Execution::make_unique_lock (sSuppressDuplicatesThreshold_) };
    if (sSuppressDuplicatesThreshold_ != suppressDuplicatesThreshold) {
        sSuppressDuplicatesThreshold_ = suppressDuplicatesThreshold;
        UpdateBookkeepingThread_ ();
    }
}

void    Logger::FlushDupsWarning_ ()
{
#if     USE_NOISY_TRACE_IN_THIS_MODULE_
    Debug::TraceContextBumper ctx (SDKSTR ("Logger::FlushDupsWarning_"));
#endif
    if (sLastMsg_.fRepeatCount_ > 0) {
#if     USE_NOISY_TRACE_IN_THIS_MODULE_
        DbgTrace (L"sLastMsg_.fRepeatCount_ = %d", sLastMsg_.fRepeatCount_);
#endif
        shared_ptr<IAppenderRep> tmp =   sThe_.fAppender_;   // avoid races and critical sections
        if (tmp != nullptr) {
            if (sLastMsg_.fRepeatCount_ == 1) {
                tmp->Log (sLastMsg_.fLastMsgSent_.first, sLastMsg_.fLastMsgSent_.second);
            }
            else {
                tmp->Log (sLastMsg_.fLastMsgSent_.first,  Characters::Format (L"[%d duplicates suppressed]: %s", sLastMsg_.fRepeatCount_ - 1, sLastMsg_.fLastMsgSent_.second.c_str ()));
            }
        }
        sLastMsg_.fRepeatCount_ = 0;
        sLastMsg_.fLastMsgSent_.second.clear ();
    }
}

void    Logger::UpdateBookkeepingThread_ ()
{
    sBookkeepingThread_.AbortAndWaitForDone ();
    sBookkeepingThread_ = Thread ();  // so null

    Time::DurationSecondsType   suppressDuplicatesThreshold =   sSuppressDuplicatesThreshold_->Value (0);
    bool                        suppressDuplicates          =   suppressDuplicatesThreshold > 0;
    if (suppressDuplicates or GetBufferingEnabled ()) {
        if (suppressDuplicates) {
            sBookkeepingThread_ = Thread ([suppressDuplicatesThreshold] () {
                while (true) {
                    DurationSecondsType time2Wait = max (static_cast<DurationSecondsType> (2), suppressDuplicatesThreshold);    // never wait less than this
                    try {
                        auto p = sOutMsgQ_.RemoveHead (time2Wait);
                        shared_ptr<IAppenderRep> tmp =   sThe_.fAppender_;   // avoid races and critical sections
                        if (tmp != nullptr) {
                            tmp->Log (p.first, p.second);
                        }
                    }
                    catch (const TimeOutException&) {
                    }
                    {
                        auto    critSec { make_unique_lock (sLastMsg_.fMutex_) };
                        if (sLastMsg_.fRepeatCount_ > 0 and sLastMsg_.fLastSentAt + suppressDuplicatesThreshold < Time::GetTickCount ()) {
                            FlushDupsWarning_ ();
                        }
                    }
                }
            });
        }
        else {
            sBookkeepingThread_ = Thread ([] () {
                while (true) {
                    auto p = sOutMsgQ_.RemoveHead ();
                    shared_ptr<IAppenderRep> tmp =   sThe_.fAppender_;   // avoid races and critical sections
                    if (tmp != nullptr) {
                        tmp->Log (p.first, p.second);
                    }
                }
            });
        }
        sBookkeepingThread_.SetThreadName (L"Logger Bookkeeping");
        sBookkeepingThread_.SetThreadPriority (Thread::Priority::eBelowNormal);
        sBookkeepingThread_.Start ();
    }

    // manually push out pending messages
    FlushBuffer ();
}

/*
 ********************************************************************************
 ************************** Execution::IAppenderRep *****************************
 ********************************************************************************
 */
Logger::IAppenderRep::~IAppenderRep ()
{
}

#if     qHas_Syslog
/*
 ********************************************************************************
 ************************ Execution::SysLogAppender *****************************
 ********************************************************************************
 */
namespace   {
    string mkMsg_ (const String& applicationName)
    {
        return Characters::CString::Format ("%s[%d]", applicationName.AsNarrowSDKString ().c_str (), GetCurrentProcessID ());
    }
}
Logger::SysLogAppender::SysLogAppender (const String& applicationName)
    : fApplicationName_ (mkMsg_ (applicationName))
{
    openlog (fApplicationName_.c_str (), 0, LOG_DAEMON);    // not sure what facility to pass?
}

Logger::SysLogAppender::SysLogAppender (const String& applicationName, int facility)
    : fApplicationName_ (mkMsg_ (applicationName))
{
    openlog (fApplicationName_.c_str (), 0, facility);
}

Logger::SysLogAppender::~SysLogAppender ()
{
    closelog ();
}

void    Logger::SysLogAppender::Log (Priority logLevel, const String& message)
{
    DbgTrace (L"SYSLOG: %s: %s", Stroika_Enum_Names(Priority).GetName (logLevel), message.c_str ());
    int sysLogLevel = LOG_NOTICE;   // doesnt matter cuz assert error if hit
    switch (logLevel) {
        case Priority::eDebug:
            sysLogLevel = LOG_DEBUG;
            break;
        case Priority::eInfo:
            sysLogLevel = LOG_INFO;
            break;
        case Priority::eNotice:
            sysLogLevel = LOG_NOTICE;
            break;
        case Priority::eWarning:
            sysLogLevel = LOG_WARNING;
            break;
        case Priority::eError:
            sysLogLevel = LOG_ERR;
            break;
        case Priority::eCriticalError:
            sysLogLevel = LOG_CRIT;
            break;
        case Priority::eAlertError:
            sysLogLevel = LOG_ALERT;
            break;
        case Priority::eEmergency:
            sysLogLevel = LOG_EMERG;
            break;
        default:
            RequireNotReached ();
    }
    syslog (sysLogLevel, "%s", message.AsNarrowSDKString ().c_str ());
}
#endif

/*
 ********************************************************************************
 ************************** Execution::FileAppender *****************************
 ********************************************************************************
 */
Logger::FileAppender::FileAppender (const String& fileName)
{
    AssertNotImplemented ();
}

void    Logger::FileAppender::Log (Priority logLevel, const String& message)
{
    AssertNotImplemented ();
}

#if     qPlatform_Windows
/*
 ********************************************************************************
 ************************ Execution::SysLogAppender *****************************
 ********************************************************************************
 */
Logger::WindowsEventLogAppender::WindowsEventLogAppender ()
{
}

void    Logger::WindowsEventLogAppender::Log (Priority logLevel, const String& message)
{
    /*
     * VERY QUICK HACK - AT LEAST DUMPS SOME INFO TO EVENTLOG - BUT MUCH TWEAKING LEFT TODO
     */
    const   TCHAR   kEventSourceName[]  =   _T ("xxxtest");
    WORD    eventType   =   EVENTLOG_ERROR_TYPE;
    switch (logLevel) {
        case Priority::eDebug:
            eventType = EVENTLOG_INFORMATION_TYPE;
            break;
        case Priority::eInfo:
            eventType = EVENTLOG_INFORMATION_TYPE;
            break;
        case Priority::eNotice:
            eventType = EVENTLOG_INFORMATION_TYPE;
            break;
        case Priority::eWarning:
            eventType = EVENTLOG_WARNING_TYPE;
            break;
        case Priority::eError:
            eventType = EVENTLOG_ERROR_TYPE;
            break;
        case Priority::eAlertError:
            eventType = EVENTLOG_ERROR_TYPE;
            break;
        case Priority::eEmergency:
            eventType = EVENTLOG_ERROR_TYPE;
            break;
    }
#define CATEGORY_Normal                  0x00000001L
    WORD    eventCategoryID =   CATEGORY_Normal;
    // See SPR#565 for wierdness - where I cannot really get these paid attention to
    // by the Windows EventLog. So had to use the .Net eventlogger. It SEEMS
#define EVENT_Message                    0x00000064L
    const   DWORD   kEventID            =   EVENT_Message;
    HANDLE  hEventSource = RegisterEventSource (NULL, kEventSourceName);
    Verify (hEventSource != NULL);
    SDKString tmp = message.AsSDKString ();
    const Characters::SDKChar* msg = tmp.c_str ();
    Verify (::ReportEvent (
                hEventSource,
                eventType,
                eventCategoryID,
                kEventID,
                NULL,
                (WORD)1,
                0,
                &msg,
                NULL
            )
           );
    Verify (::DeregisterEventSource (hEventSource));
}
#endif<commit_msg>use Synchonized<> with recursive mutex  instead of nonrecursive mutext to avoid a flush deadlock in Execution::Logger<commit_after>/*
 * Copyright(c) Sophist Solutions, Inc. 1990-2015.  All rights reserved
 */
#include    "../StroikaPreComp.h"

#if     qHas_Syslog
#include    <syslog.h>
#endif

#include    "../Characters/CString/Utilities.h"
#include    "../Characters/Format.h"
#include    "../Debug/Trace.h"
#include    "BlockingQueue.h"
#include    "Common.h"
#include    "Process.h"
#include    "Synchronized.h"
#include    "Thread.h"
#include    "TimeOutException.h"

#include    "Logger.h"

using   namespace   Stroika::Foundation;
using   namespace   Stroika::Foundation::Configuration;
using   namespace   Stroika::Foundation::Execution;

using   Memory::Optional;
using   Time::DurationSecondsType;

// Comment this in to turn on aggressive noisy DbgTrace in this module
//#define   USE_NOISY_TRACE_IN_THIS_MODULE_       1

/*
 ********************************************************************************
 ******************************** Execution::Logger *****************************
 ********************************************************************************
 */
Logger  Logger::sThe_;

const EnumNames<Logger::Priority>   Logger::Stroika_Enum_Names(Priority)
{
    { Logger::Priority::eDebug, L"Debug" },
    { Logger::Priority::eInfo, L"Info" },
    { Logger::Priority::eNotice, L"Notice" },
    { Logger::Priority::eWarning, L"Warning" },
    { Logger::Priority::eError, L"Error" },
    { Logger::Priority::eCriticalError, L"CriticalError" },
    { Logger::Priority::eAlertError, L"AlertError" },
    { Logger::Priority::eEmergency, L"Emergency" },
};

namespace {
    BlockingQueue<pair<Logger::Priority, String>>   sOutMsgQ_;
    Execution::Thread                               sBookkeepingThread_;
    bool                                            sOutQMaybeNeedsFlush_ = true;       // sligt optimziation of not using buffering

    Synchronized<Memory::Optional<DurationSecondsType>>       sSuppressDuplicatesThreshold_;

    struct LastMsg_ {
        pair<Logger::Priority, String>      fLastMsgSent_;
        Time::DurationSecondsType           fLastSentAt = 0.0;
        unsigned int                        fRepeatCount_ = 0;
    };
    Synchronized<LastMsg_>    sLastMsg_;
}

Logger::Logger ()
    : fAppender_ ()
    , fMinLogLevel_ (Priority::eInfo)
    , fBufferingEnabled_ (false)
{
}

void    Logger::SetAppender (const shared_ptr<IAppenderRep>& rep)
{
    fAppender_ = rep;
}

void    Logger::Log_ (Priority logLevel, const String& format, va_list argList)
{
    shared_ptr<IAppenderRep> tmp =   sThe_.fAppender_;   // avoid races and critical sections
    if (tmp.get () != nullptr) {
        auto p = pair<Logger::Priority, String> (logLevel, Characters::FormatV (format.c_str (), argList));
        if (sSuppressDuplicatesThreshold_->IsPresent ()) {
            auto    lastMsgLocked = sLastMsg_.GetReference ();
            if (p == lastMsgLocked->fLastMsgSent_) {
                lastMsgLocked->fRepeatCount_++;
                lastMsgLocked->fLastSentAt = Time::GetTickCount ();
                return; // so will be handled later
            }
            else {
                if (lastMsgLocked->fRepeatCount_ > 0) {
                    FlushDupsWarning_ ();
                }
                lastMsgLocked->fLastMsgSent_ = p;
                lastMsgLocked->fRepeatCount_ = 0;
                lastMsgLocked->fLastSentAt = Time::GetTickCount ();
            }
        }
        if (GetBufferingEnabled ()) {
            sOutQMaybeNeedsFlush_ = true;
            sOutMsgQ_.AddTail (p);
        }
        else {
            if (sOutQMaybeNeedsFlush_) {
                FlushBuffer (); // in case recently disabled
            }
            tmp->Log (p.first, p.second);
        }
    }
}

void        Logger::SetBufferingEnabled (bool logBufferingEnabled)
{
#if     USE_NOISY_TRACE_IN_THIS_MODULE_
    Debug::TraceContextBumper ctx (SDKSTR ("Logger::SetBufferingEnabled"));
    DbgTrace (L"(logBufferingEnabled=%d)", logBufferingEnabled);
#endif
    sThe_.fBufferingEnabled_ = logBufferingEnabled;
    UpdateBookkeepingThread_ ();
}

void        Logger::FlushBuffer ()
{
    shared_ptr<IAppenderRep> tmp =   sThe_.fAppender_;   // avoid races and critical sections
    if (tmp != nullptr) {
        while (true) {
            Optional<pair<Logger::Priority, String>> p = sOutMsgQ_.RemoveHeadIfPossible ();
            if (p.IsPresent ()) {
                tmp->Log (p->first, p->second);
            }
            else {
                return; // no more entries
            }
        }
    }
    sOutQMaybeNeedsFlush_ = false;
}

Memory::Optional<Time::DurationSecondsType> Logger::GetSuppressDuplicates ()
{
    return Memory::Optional<Time::DurationSecondsType> (sSuppressDuplicatesThreshold_);
}

void    Logger::SetSuppressDuplicates (const Memory::Optional<DurationSecondsType>& suppressDuplicatesThreshold)
{
#if     USE_NOISY_TRACE_IN_THIS_MODULE_
    Debug::TraceContextBumper ctx (SDKSTR ("Logger::SetSuppressDuplicates"));
    DbgTrace (L"(suppressDuplicatesThreshold=%f)", suppressDuplicatesThreshold.Value (-1));
#endif
    Require (suppressDuplicatesThreshold.IsMissing () or * suppressDuplicatesThreshold > 0.0);
    auto    critSec { Execution::make_unique_lock (sSuppressDuplicatesThreshold_) };
    if (sSuppressDuplicatesThreshold_ != suppressDuplicatesThreshold) {
        sSuppressDuplicatesThreshold_ = suppressDuplicatesThreshold;
        UpdateBookkeepingThread_ ();
    }
}

void    Logger::FlushDupsWarning_ ()
{
#if     USE_NOISY_TRACE_IN_THIS_MODULE_
    Debug::TraceContextBumper ctx (SDKSTR ("Logger::FlushDupsWarning_"));
#endif
    auto    lastMsgLocked = sLastMsg_.GetReference ();
    if (lastMsgLocked->fRepeatCount_ > 0) {
#if     USE_NOISY_TRACE_IN_THIS_MODULE_
        DbgTrace (L"sLastMsg_.fRepeatCount_ = %d", lastMsgLocked->fRepeatCount_);
#endif
        shared_ptr<IAppenderRep> tmp =   sThe_.fAppender_;   // avoid races and critical sections
        if (tmp != nullptr) {
            if (lastMsgLocked->fRepeatCount_ == 1) {
                tmp->Log (lastMsgLocked->fLastMsgSent_.first, lastMsgLocked->fLastMsgSent_.second);
            }
            else {
                tmp->Log (lastMsgLocked->fLastMsgSent_.first,  Characters::Format (L"[%d duplicates suppressed]: %s", lastMsgLocked->fRepeatCount_ - 1, lastMsgLocked->fLastMsgSent_.second.c_str ()));
            }
        }
        lastMsgLocked->fRepeatCount_ = 0;
        lastMsgLocked->fLastMsgSent_.second.clear ();
    }
}

void    Logger::UpdateBookkeepingThread_ ()
{
    sBookkeepingThread_.AbortAndWaitForDone ();
    sBookkeepingThread_ = Thread ();  // so null

    Time::DurationSecondsType   suppressDuplicatesThreshold =   sSuppressDuplicatesThreshold_->Value (0);
    bool                        suppressDuplicates          =   suppressDuplicatesThreshold > 0;
    if (suppressDuplicates or GetBufferingEnabled ()) {
        if (suppressDuplicates) {
            sBookkeepingThread_ = Thread ([suppressDuplicatesThreshold] () {
                while (true) {
                    DurationSecondsType time2Wait = max (static_cast<DurationSecondsType> (2), suppressDuplicatesThreshold);    // never wait less than this
                    try {
                        auto p = sOutMsgQ_.RemoveHead (time2Wait);
                        shared_ptr<IAppenderRep> tmp =   sThe_.fAppender_;   // avoid races and critical sections
                        if (tmp != nullptr) {
                            tmp->Log (p.first, p.second);
                        }
                    }
                    catch (const TimeOutException&) {
                    }
                    {
                        auto    lastMsgLocked = sLastMsg_.GetReference ();
                        if (lastMsgLocked->fRepeatCount_ > 0 and lastMsgLocked->fLastSentAt + suppressDuplicatesThreshold < Time::GetTickCount ()) {
                            FlushDupsWarning_ ();
                        }
                    }
                }
            });
        }
        else {
            sBookkeepingThread_ = Thread ([] () {
                while (true) {
                    auto p = sOutMsgQ_.RemoveHead ();
                    shared_ptr<IAppenderRep> tmp =   sThe_.fAppender_;   // avoid races and critical sections
                    if (tmp != nullptr) {
                        tmp->Log (p.first, p.second);
                    }
                }
            });
        }
        sBookkeepingThread_.SetThreadName (L"Logger Bookkeeping");
        sBookkeepingThread_.SetThreadPriority (Thread::Priority::eBelowNormal);
        sBookkeepingThread_.Start ();
    }

    // manually push out pending messages
    FlushBuffer ();
}

/*
 ********************************************************************************
 ************************** Execution::IAppenderRep *****************************
 ********************************************************************************
 */
Logger::IAppenderRep::~IAppenderRep ()
{
}

#if     qHas_Syslog
/*
 ********************************************************************************
 ************************ Execution::SysLogAppender *****************************
 ********************************************************************************
 */
namespace   {
    string mkMsg_ (const String& applicationName)
    {
        return Characters::CString::Format ("%s[%d]", applicationName.AsNarrowSDKString ().c_str (), GetCurrentProcessID ());
    }
}
Logger::SysLogAppender::SysLogAppender (const String& applicationName)
    : fApplicationName_ (mkMsg_ (applicationName))
{
    openlog (fApplicationName_.c_str (), 0, LOG_DAEMON);    // not sure what facility to pass?
}

Logger::SysLogAppender::SysLogAppender (const String& applicationName, int facility)
    : fApplicationName_ (mkMsg_ (applicationName))
{
    openlog (fApplicationName_.c_str (), 0, facility);
}

Logger::SysLogAppender::~SysLogAppender ()
{
    closelog ();
}

void    Logger::SysLogAppender::Log (Priority logLevel, const String& message)
{
    DbgTrace (L"SYSLOG: %s: %s", Stroika_Enum_Names(Priority).GetName (logLevel), message.c_str ());
    int sysLogLevel = LOG_NOTICE;   // doesnt matter cuz assert error if hit
    switch (logLevel) {
        case Priority::eDebug:
            sysLogLevel = LOG_DEBUG;
            break;
        case Priority::eInfo:
            sysLogLevel = LOG_INFO;
            break;
        case Priority::eNotice:
            sysLogLevel = LOG_NOTICE;
            break;
        case Priority::eWarning:
            sysLogLevel = LOG_WARNING;
            break;
        case Priority::eError:
            sysLogLevel = LOG_ERR;
            break;
        case Priority::eCriticalError:
            sysLogLevel = LOG_CRIT;
            break;
        case Priority::eAlertError:
            sysLogLevel = LOG_ALERT;
            break;
        case Priority::eEmergency:
            sysLogLevel = LOG_EMERG;
            break;
        default:
            RequireNotReached ();
    }
    syslog (sysLogLevel, "%s", message.AsNarrowSDKString ().c_str ());
}
#endif

/*
 ********************************************************************************
 ************************** Execution::FileAppender *****************************
 ********************************************************************************
 */
Logger::FileAppender::FileAppender (const String& fileName)
{
    AssertNotImplemented ();
}

void    Logger::FileAppender::Log (Priority logLevel, const String& message)
{
    AssertNotImplemented ();
}

#if     qPlatform_Windows
/*
 ********************************************************************************
 ************************ Execution::SysLogAppender *****************************
 ********************************************************************************
 */
Logger::WindowsEventLogAppender::WindowsEventLogAppender ()
{
}

void    Logger::WindowsEventLogAppender::Log (Priority logLevel, const String& message)
{
    /*
     * VERY QUICK HACK - AT LEAST DUMPS SOME INFO TO EVENTLOG - BUT MUCH TWEAKING LEFT TODO
     */
    const   TCHAR   kEventSourceName[]  =   _T ("xxxtest");
    WORD    eventType   =   EVENTLOG_ERROR_TYPE;
    switch (logLevel) {
        case Priority::eDebug:
            eventType = EVENTLOG_INFORMATION_TYPE;
            break;
        case Priority::eInfo:
            eventType = EVENTLOG_INFORMATION_TYPE;
            break;
        case Priority::eNotice:
            eventType = EVENTLOG_INFORMATION_TYPE;
            break;
        case Priority::eWarning:
            eventType = EVENTLOG_WARNING_TYPE;
            break;
        case Priority::eError:
            eventType = EVENTLOG_ERROR_TYPE;
            break;
        case Priority::eAlertError:
            eventType = EVENTLOG_ERROR_TYPE;
            break;
        case Priority::eEmergency:
            eventType = EVENTLOG_ERROR_TYPE;
            break;
    }
#define CATEGORY_Normal                  0x00000001L
    WORD    eventCategoryID =   CATEGORY_Normal;
    // See SPR#565 for wierdness - where I cannot really get these paid attention to
    // by the Windows EventLog. So had to use the .Net eventlogger. It SEEMS
#define EVENT_Message                    0x00000064L
    const   DWORD   kEventID            =   EVENT_Message;
    HANDLE  hEventSource = RegisterEventSource (NULL, kEventSourceName);
    Verify (hEventSource != NULL);
    SDKString tmp = message.AsSDKString ();
    const Characters::SDKChar* msg = tmp.c_str ();
    Verify (::ReportEvent (
                hEventSource,
                eventType,
                eventCategoryID,
                kEventID,
                NULL,
                (WORD)1,
                0,
                &msg,
                NULL
            )
           );
    Verify (::DeregisterEventSource (hEventSource));
}
#endif<|endoftext|>


<commit_before>/*
 * Copyright (C) 2013 by Glenn Hickey (hickey@soe.ucsc.edu)
 *
 * Released under the MIT license, see LICENSE.txt
 */

#include <iostream>
#include <string>
#include <set>
#include <vector>
#include <map>
#include "hal.h"
#include "halBlockMapper.h"

using namespace hal;
using namespace std;

hal_size_t BlockMapper::_maxAdjScan = 5;

BlockMapper::BlockMapper()
{

}

BlockMapper::~BlockMapper()
{
  erase();
}

void BlockMapper::erase()
{
  _segMap.clear();
  _spanningTree.clear();
}

void BlockMapper::init(const Genome* refGenome, const Genome* queryGenome,
                       hal_index_t absRefFirst, hal_index_t absRefLast,
                       bool doDupes, hal_size_t minLength,
                       bool mapTargetAdjacencies)
{
  erase();
  _absRefFirst = absRefFirst;
  _absRefLast = absRefLast;
  _doDupes = doDupes;
  _minLength = minLength;
  _mapAdj = mapTargetAdjacencies;
  _refGenome = refGenome;
  _refSequence = refGenome->getSequenceBySite(_absRefFirst);
  assert(_refSequence == refGenome->getSequenceBySite(_absRefLast));
  _queryGenome = queryGenome;

  set<const Genome*> inputSet;
  inputSet.insert(_refGenome);
  inputSet.insert(_queryGenome);
  getGenomesInSpanningTree(inputSet, _spanningTree);
}

void BlockMapper::map()
{
  SegmentIteratorConstPtr refSeg;
  hal_index_t lastIndex;
  if (_refGenome->getNumBottomSegments() > 0)
  {
    refSeg = _refGenome->getBottomSegmentIterator();
    lastIndex = _refGenome->getNumBottomSegments();
  }
  else
  {
    refSeg = _refGenome->getTopSegmentIterator();
    lastIndex = _refGenome->getNumTopSegments();
  }

  refSeg->toSite(_absRefFirst, false);
  hal_offset_t startOffset = _absRefFirst - refSeg->getStartPosition();
  hal_offset_t endOffset = 0;
  if (_absRefLast <= refSeg->getEndPosition())
  {
    endOffset = refSeg->getEndPosition() - _absRefLast;
  }
  refSeg->slice(startOffset, endOffset);

  assert(refSeg->getStartPosition() ==  _absRefFirst);
  assert(refSeg->getEndPosition() <= _absRefLast);

  while (refSeg->getArrayIndex() < lastIndex &&
         refSeg->getStartPosition() <= _absRefLast)
  {
    refSeg->getMappedSegments(_segMap, _queryGenome, &_spanningTree,
                              _doDupes, _minLength);
    refSeg->toRight(_absRefLast);
  }

  if (_mapAdj)
  {
    // construct a set sorted on query instead of ref.
    MSFlipSet flipSet(_segMap.begin(), _segMap.end());

    MSFlipSet::const_iterator i;
    for (i = flipSet.begin(); i != flipSet.end(); ++i)
    {
      mapAdjacencies(flipSet, i);
    }
  }
}

void BlockMapper::mapAdjacencies(const MSFlipSet& flipSet,
                                 MSFlipSet::const_iterator flipIt)
{
  assert(flipSet.empty() == false && flipIt != flipSet.end());
  MappedSegmentConstPtr mappedQuerySeg = *flipIt;
  hal_index_t maxIndex;
  hal_index_t minIndex;
  SegmentIteratorConstPtr queryIt = makeIterator(mappedQuerySeg,
                                                 minIndex,
                                                 maxIndex);
  MSSet backResults;
  MSFlipSet::const_iterator flipNext = flipIt;
  if (queryIt->getReversed())
  {
    flipNext = flipNext == flipSet.begin() ? flipSet.end() : --flipNext;
  }
  else
  {
    ++flipNext;
  }
  hal_size_t iter = 0;
  queryIt->toRight();
  while (queryIt->getArrayIndex() >= minIndex &&
         queryIt->getArrayIndex() < maxIndex && iter < _maxAdjScan)
  {
    if (flipNext != flipSet.end())
    {
      // if cut returns nothing, then the region in question is covered
      // by flipNext (ie already mapped).
      cutByNext(queryIt, *flipNext, !queryIt->getReversed());
    }
    if (queryIt.get() == NULL)
    {
      break;
    }
    size_t backSize = backResults.size();
    assert(queryIt->getArrayIndex() >= 0);
    queryIt->getMappedSegments(backResults, _refGenome, &_spanningTree,
                               _doDupes, _minLength);
    // something was found, that's good enough.
    if (backResults.size() > backSize)
    {
      break;
    }
    queryIt->toRight();
    ++iter;
  }

  queryIt = makeIterator(mappedQuerySeg,
                         minIndex,
                         maxIndex);

  MSFlipSet::const_iterator flipPrev = flipIt;
  queryIt->getReversed() ? ++flipPrev : --flipPrev;
  iter = 0;
  queryIt->toLeft();
  while (queryIt->getArrayIndex() >= minIndex &&
         queryIt->getArrayIndex() < maxIndex && iter < _maxAdjScan)
  {
    if (flipNext != flipSet.end())
    {
      // if cut returns nothing, then the region in question is covered
      // by flipNext (ie already mapped).
      cutByNext(queryIt, *flipNext, queryIt->getReversed());
    }
    if (queryIt.get() == NULL)
    {
      break;
    }
    size_t backSize = backResults.size();
    queryIt->getMappedSegments(backResults, _refGenome, &_spanningTree,
                               _doDupes, _minLength);
    // something was found, that's good enough.
    if (backResults.size() > backSize)
    {
      break;
    }
    queryIt->toLeft();
    ++iter;
  }

  // flip the results and copy back to our main set.
  for (MSSet::iterator i = backResults.begin(); i != backResults.end(); ++i)
  {
    MappedSegmentConstPtr mseg = *i;
    mseg->flip();
    assert(_segMap.find(mseg) == _segMap.end());
    _segMap.insert(mseg);
  }
}

SegmentIteratorConstPtr BlockMapper::makeIterator(
  MappedSegmentConstPtr mappedSegment, hal_index_t& minIndex,
  hal_index_t& maxIndex)
{
  SegmentIteratorConstPtr segIt;
  if (mappedSegment->isTop())
  {
    segIt = mappedSegment->getGenome()->getTopSegmentIterator(
      mappedSegment->getArrayIndex());
    minIndex = segIt->getSequence()->getTopSegmentArrayIndex();
    maxIndex = minIndex +
       (hal_index_t)segIt->getSequence()->getNumTopSegments();
  }
  else
  {
    segIt = mappedSegment->getGenome()->getBottomSegmentIterator(
      mappedSegment->getArrayIndex());
    minIndex = segIt->getSequence()->getBottomSegmentArrayIndex();
    maxIndex = minIndex +
       (hal_index_t)segIt->getSequence()->getNumBottomSegments();
  }

  if (mappedSegment->getReversed())
  {
    segIt->toReverse();
  }
  segIt->slice(mappedSegment->getStartOffset(),
               mappedSegment->getEndOffset());

  assert(segIt->getGenome() == mappedSegment->getGenome());
  assert(segIt->getArrayIndex() == mappedSegment->getArrayIndex());
  assert(segIt->getStartOffset() == mappedSegment->getStartOffset());
  assert(segIt->getEndOffset() == mappedSegment->getEndOffset());
  assert(segIt->getReversed() == mappedSegment->getReversed());

  return segIt;
}

void BlockMapper::cutByNext(SegmentIteratorConstPtr queryIt,
                            SlicedSegmentConstPtr nextSeg,
                            bool right)
{
  assert(queryIt->getGenome() == nextSeg->getGenome());
  assert(queryIt->isTop() == nextSeg->isTop());

  if (queryIt->getArrayIndex() == nextSeg->getArrayIndex())
  {
    hal_offset_t so1 = queryIt->getStartOffset();
    hal_offset_t eo1 = queryIt->getEndOffset();
    if (queryIt->getReversed())
    {
      swap(so1, eo1);
    }
    hal_offset_t so2 = nextSeg->getReversed() ? nextSeg->getEndOffset() :
       nextSeg->getStartOffset();

    if (right)
    {
      assert(max(nextSeg->getStartPosition(), nextSeg->getEndPosition()) >=
             max(queryIt->getStartPosition(), queryIt->getEndPosition()));

      // overlap on start position.  we zap
      if (so1 >= so2)
      {
        queryIt = SegmentIteratorConstPtr();
      }
      else
      {
        assert(max(nextSeg->getStartPosition(), nextSeg->getEndPosition()) >
               max(queryIt->getStartPosition(), queryIt->getEndPosition()));

        hal_index_t e1 = max(queryIt->getEndPosition(),
                             queryIt->getStartPosition());
        hal_index_t s2 = min(nextSeg->getEndPosition(),
                             nextSeg->getStartPosition());
        // end position of queryIt overlaps next seg.  so we cut it.
        if (e1 >= s2)
        {
          hal_index_t delta = 1 + e1 - s2;
          assert(delta < (hal_index_t)queryIt->getLength());
          hal_offset_t newEndOffset = eo1 + delta;
          hal_offset_t newStartOffset = so1;
          if (queryIt->getReversed() == true)
          {
            swap(newEndOffset, newStartOffset);
          }
          queryIt->slice(newStartOffset, newEndOffset);
        }
      }
      assert(!queryIt.get() ||
             max(queryIt->getStartPosition(), queryIt->getEndPosition()) <
             min(nextSeg->getStartPosition(), nextSeg->getEndPosition()));
    }
    else
    {
      assert(min(nextSeg->getStartPosition(), nextSeg->getEndPosition()) <=
             min(queryIt->getStartPosition(), queryIt->getEndPosition()));

      hal_index_t s1 = min(queryIt->getEndPosition(),
                           queryIt->getStartPosition());
      hal_index_t e1 = max(queryIt->getEndPosition(),
                           queryIt->getStartPosition());
      hal_index_t e2 = max(nextSeg->getEndPosition(),
                           nextSeg->getStartPosition());

      // overlap on end position.  we zap
      if (e1 <= e2)
      {
        queryIt = SegmentIteratorConstPtr();
      }
      else
      {
        assert(min(nextSeg->getStartPosition(), nextSeg->getEndPosition()) <
               min(queryIt->getStartPosition(), queryIt->getEndPosition()));

        // end position of queryIt overlaps next seg.  so we cut it.
        if (s1 <= e2)
        {
          hal_index_t delta = 1 + e2 - s1;
          assert(delta < (hal_index_t)queryIt->getLength());
          hal_offset_t newStartOffset = so1 + delta;
          hal_offset_t newEndOffset = eo1;
          if (queryIt->getReversed() == true)
          {
            swap(newEndOffset, newStartOffset);
          }
          queryIt->slice(newStartOffset, newEndOffset);
        }
      }
      assert(!queryIt.get() ||
             min(queryIt->getStartPosition(), queryIt->getEndPosition()) >
             max(nextSeg->getStartPosition(), nextSeg->getEndPosition()));
    }
  }
}
<commit_msg>flip back-mapped adjacent reference segments to forward strand.  filter out back-mapped segements that map outside of reference chromosome<commit_after>/*
 * Copyright (C) 2013 by Glenn Hickey (hickey@soe.ucsc.edu)
 *
 * Released under the MIT license, see LICENSE.txt
 */

#include <iostream>
#include <string>
#include <set>
#include <vector>
#include <map>
#include "hal.h"
#include "halBlockMapper.h"

using namespace hal;
using namespace std;

hal_size_t BlockMapper::_maxAdjScan = 5;

BlockMapper::BlockMapper()
{

}

BlockMapper::~BlockMapper()
{
  erase();
}

void BlockMapper::erase()
{
  _segMap.clear();
  _spanningTree.clear();
}

void BlockMapper::init(const Genome* refGenome, const Genome* queryGenome,
                       hal_index_t absRefFirst, hal_index_t absRefLast,
                       bool doDupes, hal_size_t minLength,
                       bool mapTargetAdjacencies)
{
  erase();
  _absRefFirst = absRefFirst;
  _absRefLast = absRefLast;
  _doDupes = doDupes;
  _minLength = minLength;
  _mapAdj = mapTargetAdjacencies;
  _refGenome = refGenome;
  _refSequence = refGenome->getSequenceBySite(_absRefFirst);
  assert(_refSequence == refGenome->getSequenceBySite(_absRefLast));
  _queryGenome = queryGenome;

  set<const Genome*> inputSet;
  inputSet.insert(_refGenome);
  inputSet.insert(_queryGenome);
  getGenomesInSpanningTree(inputSet, _spanningTree);
}

void BlockMapper::map()
{
  SegmentIteratorConstPtr refSeg;
  hal_index_t lastIndex;
  if (_refGenome->getNumBottomSegments() > 0)
  {
    refSeg = _refGenome->getBottomSegmentIterator();
    lastIndex = _refGenome->getNumBottomSegments();
  }
  else
  {
    refSeg = _refGenome->getTopSegmentIterator();
    lastIndex = _refGenome->getNumTopSegments();
  }

  refSeg->toSite(_absRefFirst, false);
  hal_offset_t startOffset = _absRefFirst - refSeg->getStartPosition();
  hal_offset_t endOffset = 0;
  if (_absRefLast <= refSeg->getEndPosition())
  {
    endOffset = refSeg->getEndPosition() - _absRefLast;
  }
  refSeg->slice(startOffset, endOffset);

  assert(refSeg->getStartPosition() ==  _absRefFirst);
  assert(refSeg->getEndPosition() <= _absRefLast);

  while (refSeg->getArrayIndex() < lastIndex &&
         refSeg->getStartPosition() <= _absRefLast)
  {
    refSeg->getMappedSegments(_segMap, _queryGenome, &_spanningTree,
                              _doDupes, _minLength);
    refSeg->toRight(_absRefLast);
  }

  if (_mapAdj)
  {
    // construct a set sorted on query instead of ref.
    MSFlipSet flipSet(_segMap.begin(), _segMap.end());

    MSFlipSet::const_iterator i;
    for (i = flipSet.begin(); i != flipSet.end(); ++i)
    {
      mapAdjacencies(flipSet, i);
    }
  }
}

void BlockMapper::mapAdjacencies(const MSFlipSet& flipSet,
                                 MSFlipSet::const_iterator flipIt)
{
  assert(flipSet.empty() == false && flipIt != flipSet.end());
  MappedSegmentConstPtr mappedQuerySeg = *flipIt;
  hal_index_t maxIndex;
  hal_index_t minIndex;
  SegmentIteratorConstPtr queryIt = makeIterator(mappedQuerySeg,
                                                 minIndex,
                                                 maxIndex);
  MSSet backResults;
  MSFlipSet::const_iterator flipNext = flipIt;
  if (queryIt->getReversed())
  {
    flipNext = flipNext == flipSet.begin() ? flipSet.end() : --flipNext;
  }
  else
  {
    ++flipNext;
  }
  hal_size_t iter = 0;
  queryIt->toRight();
  while (queryIt->getArrayIndex() >= minIndex &&
         queryIt->getArrayIndex() < maxIndex && iter < _maxAdjScan)
  {
    if (flipNext != flipSet.end())
    {
      // if cut returns nothing, then the region in question is covered
      // by flipNext (ie already mapped).
      cutByNext(queryIt, *flipNext, !queryIt->getReversed());
    }
    if (queryIt.get() == NULL)
    {
      break;
    }
    size_t backSize = backResults.size();
    assert(queryIt->getArrayIndex() >= 0);
    queryIt->getMappedSegments(backResults, _refGenome, &_spanningTree,
                               _doDupes, _minLength);
    // something was found, that's good enough.
    if (backResults.size() > backSize)
    {
      break;
    }
    queryIt->toRight();
    ++iter;
  }

  queryIt = makeIterator(mappedQuerySeg,
                         minIndex,
                         maxIndex);

  MSFlipSet::const_iterator flipPrev = flipIt;
  queryIt->getReversed() ? ++flipPrev : --flipPrev;
  iter = 0;
  queryIt->toLeft();
  while (queryIt->getArrayIndex() >= minIndex &&
         queryIt->getArrayIndex() < maxIndex && iter < _maxAdjScan)
  {
    if (flipNext != flipSet.end())
    {
      // if cut returns nothing, then the region in question is covered
      // by flipNext (ie already mapped).
      cutByNext(queryIt, *flipNext, queryIt->getReversed());
    }
    if (queryIt.get() == NULL)
    {
      break;
    }
    size_t backSize = backResults.size();
    queryIt->getMappedSegments(backResults, _refGenome, &_spanningTree,
                               _doDupes, _minLength);
    // something was found, that's good enough.
    if (backResults.size() > backSize)
    {
      break;
    }
    queryIt->toLeft();
    ++iter;
  }

  // flip the results and copy back to our main set.
  for (MSSet::iterator i = backResults.begin(); i != backResults.end(); ++i)
  {
    MappedSegmentConstPtr mseg = *i;
    if (mseg->getSequence() == _refSequence)
    {
      mseg->flip();
      SlicedSegmentConstPtr refSeg = mseg->getSource();
      if (refSeg->getReversed())
      {
        refSeg->slice(refSeg->getEndOffset(), refSeg->getEndOffset());
        refSeg->toReverse();
        mseg->slice(mseg->getEndOffset(), mseg->getEndOffset());
        mseg->toReverse();
      }
      assert(_segMap.find(mseg) == _segMap.end());
      _segMap.insert(mseg);
    }
  }
}

SegmentIteratorConstPtr BlockMapper::makeIterator(
  MappedSegmentConstPtr mappedSegment, hal_index_t& minIndex,
  hal_index_t& maxIndex)
{
  SegmentIteratorConstPtr segIt;
  if (mappedSegment->isTop())
  {
    segIt = mappedSegment->getGenome()->getTopSegmentIterator(
      mappedSegment->getArrayIndex());
    minIndex = segIt->getSequence()->getTopSegmentArrayIndex();
    maxIndex = minIndex +
       (hal_index_t)segIt->getSequence()->getNumTopSegments();
  }
  else
  {
    segIt = mappedSegment->getGenome()->getBottomSegmentIterator(
      mappedSegment->getArrayIndex());
    minIndex = segIt->getSequence()->getBottomSegmentArrayIndex();
    maxIndex = minIndex +
       (hal_index_t)segIt->getSequence()->getNumBottomSegments();
  }

  if (mappedSegment->getReversed())
  {
    segIt->toReverse();
  }
  segIt->slice(mappedSegment->getStartOffset(),
               mappedSegment->getEndOffset());

  assert(segIt->getGenome() == mappedSegment->getGenome());
  assert(segIt->getArrayIndex() == mappedSegment->getArrayIndex());
  assert(segIt->getStartOffset() == mappedSegment->getStartOffset());
  assert(segIt->getEndOffset() == mappedSegment->getEndOffset());
  assert(segIt->getReversed() == mappedSegment->getReversed());

  return segIt;
}

void BlockMapper::cutByNext(SegmentIteratorConstPtr queryIt,
                            SlicedSegmentConstPtr nextSeg,
                            bool right)
{
  assert(queryIt->getGenome() == nextSeg->getGenome());
  assert(queryIt->isTop() == nextSeg->isTop());

  if (queryIt->getArrayIndex() == nextSeg->getArrayIndex())
  {
    hal_offset_t so1 = queryIt->getStartOffset();
    hal_offset_t eo1 = queryIt->getEndOffset();
    if (queryIt->getReversed())
    {
      swap(so1, eo1);
    }
    hal_offset_t so2 = nextSeg->getReversed() ? nextSeg->getEndOffset() :
       nextSeg->getStartOffset();

    if (right)
    {
      assert(max(nextSeg->getStartPosition(), nextSeg->getEndPosition()) >=
             max(queryIt->getStartPosition(), queryIt->getEndPosition()));

      // overlap on start position.  we zap
      if (so1 >= so2)
      {
        queryIt = SegmentIteratorConstPtr();
      }
      else
      {
        assert(max(nextSeg->getStartPosition(), nextSeg->getEndPosition()) >
               max(queryIt->getStartPosition(), queryIt->getEndPosition()));

        hal_index_t e1 = max(queryIt->getEndPosition(),
                             queryIt->getStartPosition());
        hal_index_t s2 = min(nextSeg->getEndPosition(),
                             nextSeg->getStartPosition());
        // end position of queryIt overlaps next seg.  so we cut it.
        if (e1 >= s2)
        {
          hal_index_t delta = 1 + e1 - s2;
          assert(delta < (hal_index_t)queryIt->getLength());
          hal_offset_t newEndOffset = eo1 + delta;
          hal_offset_t newStartOffset = so1;
          if (queryIt->getReversed() == true)
          {
            swap(newEndOffset, newStartOffset);
          }
          queryIt->slice(newStartOffset, newEndOffset);
        }
      }
      assert(!queryIt.get() ||
             max(queryIt->getStartPosition(), queryIt->getEndPosition()) <
             min(nextSeg->getStartPosition(), nextSeg->getEndPosition()));
    }
    else
    {
      assert(min(nextSeg->getStartPosition(), nextSeg->getEndPosition()) <=
             min(queryIt->getStartPosition(), queryIt->getEndPosition()));

      hal_index_t s1 = min(queryIt->getEndPosition(),
                           queryIt->getStartPosition());
      hal_index_t e1 = max(queryIt->getEndPosition(),
                           queryIt->getStartPosition());
      hal_index_t e2 = max(nextSeg->getEndPosition(),
                           nextSeg->getStartPosition());

      // overlap on end position.  we zap
      if (e1 <= e2)
      {
        queryIt = SegmentIteratorConstPtr();
      }
      else
      {
        assert(min(nextSeg->getStartPosition(), nextSeg->getEndPosition()) <
               min(queryIt->getStartPosition(), queryIt->getEndPosition()));

        // end position of queryIt overlaps next seg.  so we cut it.
        if (s1 <= e2)
        {
          hal_index_t delta = 1 + e2 - s1;
          assert(delta < (hal_index_t)queryIt->getLength());
          hal_offset_t newStartOffset = so1 + delta;
          hal_offset_t newEndOffset = eo1;
          if (queryIt->getReversed() == true)
          {
            swap(newEndOffset, newStartOffset);
          }
          queryIt->slice(newStartOffset, newEndOffset);
        }
      }
      assert(!queryIt.get() ||
             min(queryIt->getStartPosition(), queryIt->getEndPosition()) >
             max(nextSeg->getStartPosition(), nextSeg->getEndPosition()));
    }
  }
}
<|endoftext|>