<commit_before>/*=========================================================================
   Library:   TubeTK

   Copyright 2010 Kitware Inc. 28 Corporate Drive,
   Clifton Park, NY, 12065, USA.

   All rights reserved.

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
=========================================================================*/
#include <iostream>
#include <sstream>

#include "itkTimeProbesCollectorBase.h"
#include "tubeMessage.h"

#include "tubeMacro.h"
#include "metaScene.h"

#include "itkSpatialObjectReader.h"
#include "itkSpatialObjectWriter.h"
#include "itkGroupSpatialObject.h"

#include "ClipTubesCLP.h"

#include <vtkCubeSource.h>
#include <vtkPolyDataWriter.h>
#include <vtkSmartPointer.h>
#include <vtkNew.h>

template< unsigned int DimensionT >
bool isInside( itk::Point< double, DimensionT > pointPos, double tubeRadius,
  itk::Vector< double, DimensionT > boxPos,
  itk::Vector< double, DimensionT > boxSize,
  std::vector<  typename itk::VesselTubeSpatialObjectPoint
    < DimensionT >::CovariantVectorType > normalList )
{
  // Return a boolean indicating if any slice of the tube
  // is included in the box.
  // A slice is considered as a point and an associated radius
  for( unsigned int i = 0; i < normalList.size(); i++ )
    {
    bool hasXInside = false;
    bool hasYInside = false;
    bool hasZInside = false;
    if( pointPos[0] + tubeRadius * normalList[i][0] >= boxPos[0] &&
      pointPos[0] - tubeRadius * normalList[i][0] <= boxPos[0] + boxSize[0] )
      {
      hasXInside = true;
      }
    if( pointPos[1] + tubeRadius * normalList[i][1] >= boxPos[1] &&
      pointPos[1] - tubeRadius * normalList[i][1] <= boxPos[1] + boxSize[1] )
      {
      hasYInside = true;
      }
    switch( DimensionT )
      {
      case 2:
        {
        hasZInside = true;
        break;
        }
      case 3:
        {
        if( pointPos[2] + tubeRadius  * normalList[i][2] >= boxPos[2] &&
          pointPos[2] - tubeRadius * normalList[i][2] <=
            boxPos[2] + boxSize[2] )
          {
          hasZInside = true;
          }
        break;
        }
      default:
        {
        tubeErrorMacro(
          << "Error: Only 2D and 3D data is currently supported." );
        return EXIT_FAILURE;
        }
      }
    if( hasXInside && hasYInside && hasZInside )
      {
      return true;
      }
    }
  return false;
}

template< unsigned int DimensionT >
void writeBox( std::vector< double > boxPos, std::vector< double > boxSize,
  std::vector< double > spacing, std::string boxFileName )
{
  if( DimensionT == 3 )
    {
    vtkSmartPointer<vtkCubeSource> cubeSource =
      vtkSmartPointer<vtkCubeSource>::New();
    cubeSource->SetBounds(
      -spacing[0] * (boxPos[0] + boxSize[0]), -spacing[0] * boxPos[0],
      -spacing[1] * (boxPos[1] + boxSize[1]), -spacing[1] * boxPos[1],
      spacing[2] * boxPos[2], spacing[2] * (boxPos[2] + boxSize[2]) );
    vtkNew<vtkPolyDataWriter> writer;
    writer->SetFileName( boxFileName.c_str() );
    writer->SetInputConnection( cubeSource->GetOutputPort() );
    writer->Write();
    }
  else
    {
    vtkSmartPointer< vtkCubeSource > cubeSource =
      vtkSmartPointer< vtkCubeSource >::New();
    cubeSource->SetBounds(
      -spacing[0] * (boxPos[0] + boxSize[0]), -spacing[0] * boxPos[0],
      -spacing[1] * (boxPos[1] + boxSize[1]), -spacing[1] * boxPos[1],
      0, 0);
    vtkNew<vtkPolyDataWriter> writer;
    writer->SetFileName( boxFileName.c_str() );
    writer->SetInputConnection( cubeSource->GetOutputPort() );
    writer->Write();
    }
}

template< unsigned int DimensionT >
int DoIt (int argc, char * argv[])
{
  PARSE_ARGS;

  // Ensure that the input image dimension is valid
  // We only support 2D and 3D Images due to the
  // limitation of itkTubeSpatialObject
  if( DimensionT != 2 && DimensionT != 3 )
    {
    tube::ErrorMessage(
      "Error: Only 2D and 3D data is currently supported.");
    return EXIT_FAILURE;
    }

  // The timeCollector to perform basic profiling of algorithmic components
  itk::TimeProbesCollectorBase timeCollector;

  // Load TRE File
  tubeStandardOutputMacro( << "\n>> Loading TRE File" );

  typedef itk::SpatialObjectReader< DimensionT >          TubesReaderType;
  typedef itk::GroupSpatialObject< DimensionT >           TubeGroupType;
  typedef itk::VesselTubeSpatialObject< DimensionT >      TubeType;
  typedef itk::VesselTubeSpatialObjectPoint< DimensionT > TubePointType;

  timeCollector.Start( "Loading Input TRE File" );

  typename TubesReaderType::Pointer tubeFileReader = TubesReaderType::New();

  try
    {
    tubeFileReader->SetFileName( inputTREFile.c_str() );
    tubeFileReader->Update();
    }
  catch( itk::ExceptionObject & err )
    {
    tube::ErrorMessage( "Error loading TRE File: "
      + std::string( err.GetDescription() ) );
    timeCollector.Report();
    return EXIT_FAILURE;
    }
  //Cast XML vector parameters
  typedef itk::Vector< double, DimensionT > VectorType;
  VectorType boxPositionVector;
  VectorType boxSizeVector;
  for( unsigned int i = 0; i < DimensionT; i++ )
    {
    boxPositionVector[i] = boxCorner[i];
    boxSizeVector[i] = boxSize[i];
    }

  typename TubeGroupType::Pointer pSourceTubeGroup =
    tubeFileReader->GetGroup();
  typename TubeGroupType::ChildrenListPointer pSourceTubeList =
    pSourceTubeGroup->GetChildren();

  timeCollector.Stop( "Loading Input TRE File" );

  // Compute clipping
  tubeStandardOutputMacro( << "\n>> Finding Tubes for Clipping" );

  timeCollector.Start( "Selecting Tubes" );
  //Target Group to save desired tubes
  typename TubeGroupType::Pointer pTargetTubeGroup = TubeGroupType::New();

  pTargetTubeGroup->CopyInformation( pSourceTubeGroup );
  // TODO: make CopyInformation of itk::SpatialObject do this
  pTargetTubeGroup->GetObjectToParentTransform()->SetScale(
    pSourceTubeGroup->GetObjectToParentTransform()->GetScale() );
  pTargetTubeGroup->GetObjectToParentTransform()->SetOffset(
    pSourceTubeGroup->GetObjectToParentTransform()->GetOffset() );
  pTargetTubeGroup->GetObjectToParentTransform()->SetMatrix(
    pSourceTubeGroup->GetObjectToParentTransform()->GetMatrix() );
  pTargetTubeGroup->SetSpacing( pSourceTubeGroup->GetSpacing() );
  pTargetTubeGroup->ComputeObjectToWorldTransform();

  int targetTubeId=0;
  std::vector< double > spacing( DimensionT, 0 );

  for( typename TubeGroupType::ChildrenListType::iterator
    tubeList_it = pSourceTubeList->begin();
    tubeList_it != pSourceTubeList->end(); ++tubeList_it)
    {
    //**** Source Tube **** :
    typename TubeType::Pointer pCurSourceTube =
      dynamic_cast< TubeType* >( tubeList_it->GetPointer() );
    //dynamic_cast verification
    if(!pCurSourceTube)
      {
      return EXIT_FAILURE;
      }
    //Compute Tangent and Normals
    pCurSourceTube->ComputeTangentAndNormals();
    //pCurSourceTube->ComputeObjectToWorldTransform();//BUG
    //Point List for TargetTube
    typename TubeType::PointListType TargetPointList;
    //Get points in current source tube
    typename TubeType::PointListType pointList =
      pCurSourceTube->GetPoints();

    //Get Index to World Transformation
    typename TubeType::TransformType * pTubeIndexPhysTransform =
      pCurSourceTube->GetIndexToWorldTransform();

    for( typename TubeType::PointListType::const_iterator
      pointList_it = pointList.begin();
      pointList_it != pointList.end(); ++pointList_it )
      {
      TubePointType curSourcePoint = *pointList_it;
      //Transform parameters in physical space
      typename TubePointType::PointType curSourcePos =
        pTubeIndexPhysTransform->TransformPoint(
          curSourcePoint.GetPosition() );
      typename TubePointType::VectorType worldBoxposition =
        pTubeIndexPhysTransform->TransformVector( boxPositionVector );
      typename TubePointType::VectorType worldBoxSize =
        pTubeIndexPhysTransform->TransformVector( boxSizeVector );
      typename TubePointType::CovariantVectorType curTubeNormal1 =
        pTubeIndexPhysTransform->TransformCovariantVector(
          curSourcePoint.GetNormal1() );
      typename TubePointType::CovariantVectorType curTubeNormal2 =
        pTubeIndexPhysTransform->TransformCovariantVector(
          curSourcePoint.GetNormal2() );
      //Save Normals in a vector to pass it as an argument for IsIside()
      std::vector<typename TubePointType::CovariantVectorType> normalList;
      normalList.push_back(curTubeNormal1);
      if( DimensionT == 3 )
        {
        normalList.push_back(curTubeNormal2);
        }
      //Save point in target tube if it belongs to the box
      if( isInside( curSourcePos, curSourcePoint.GetRadius(),
        worldBoxposition, worldBoxSize, normalList ) )
        {
        if( ClipTubes )
          {
          TargetPointList.push_back( curSourcePoint );
          }
        else
          {
          pCurSourceTube->SetId( targetTubeId );
          ++targetTubeId;
          pTargetTubeGroup->AddSpatialObject( pCurSourceTube );
          break;
          }
        }
      else
        {
        if( TargetPointList.size() > 0 )
          {
          //**** Target Tube **** :
          typename TubeType::Pointer pTargetTube = TubeType::New();

          pTargetTube->CopyInformation( pCurSourceTube );

          // TODO: make CopyInformation of itk::SpatialObject do this
          pTargetTube->GetObjectToParentTransform()->SetScale(
            pCurSourceTube->GetObjectToParentTransform()->GetScale() );
          pTargetTube->GetObjectToParentTransform()->SetOffset(
            pCurSourceTube->GetObjectToParentTransform()->GetOffset() );
          pTargetTube->GetObjectToParentTransform()->SetMatrix(
            pCurSourceTube->GetObjectToParentTransform()->GetMatrix() );
          pTargetTube->SetSpacing( pCurSourceTube->GetSpacing() );
          pTargetTube->ComputeObjectToWorldTransform();

          pTargetTube->ComputeTangentAndNormals();

          pTargetTube->SetId( targetTubeId );
          ++targetTubeId;
          //Save clipped tube
          pTargetTube->SetPoints( TargetPointList );
          pTargetTubeGroup->AddSpatialObject( pTargetTube );

          TargetPointList.clear();
          }
        }
      }
    if( TargetPointList.size() > 0 )
      {
      //**** Target Tube **** :
      typename TubeType::Pointer pTargetTube = TubeType::New();

      pTargetTube->CopyInformation( pCurSourceTube );

      // TODO: make CopyInformation of itk::SpatialObject do this
      pTargetTube->GetObjectToParentTransform()->SetScale(
        pCurSourceTube->GetObjectToParentTransform()->GetScale() );
      pTargetTube->GetObjectToParentTransform()->SetOffset(
        pCurSourceTube->GetObjectToParentTransform()->GetOffset() );
      pTargetTube->GetObjectToParentTransform()->SetMatrix(
        pCurSourceTube->GetObjectToParentTransform()->GetMatrix() );
      pTargetTube->SetSpacing( pCurSourceTube->GetSpacing() );
      pTargetTube->ComputeObjectToWorldTransform();

      pTargetTube->ComputeTangentAndNormals();

      pTargetTube->SetId( targetTubeId );
      ++targetTubeId;
      //Save clipped tube
      pTargetTube->SetPoints( TargetPointList );
      pTargetTubeGroup->AddSpatialObject( pTargetTube );

      TargetPointList.clear();
      }
    for( unsigned int d=0; d<DimensionT; ++d )
      {
      spacing[d] = pCurSourceTube->GetSpacing()[d];
      }
    }
  if( !outputBoxFile.empty() )
    {
    writeBox<DimensionT>( boxCorner, boxSize, spacing, outputBoxFile );
    }

  timeCollector.Stop( "Selecting Tubes" );

  // Write output TRE file
  tubeStandardOutputMacro(
    << "\n>> Writing TRE file" );

  timeCollector.Start( "Writing output TRE file" );

  typedef itk::SpatialObjectWriter< DimensionT > TubeWriterType;
  typename TubeWriterType::Pointer tubeWriter = TubeWriterType::New();

  try
    {
    tubeWriter->SetFileName( outputTREFile.c_str() );
    tubeWriter->SetInput(pTargetTubeGroup);
    tubeWriter->Update();
    }
  catch( itk::ExceptionObject & err )
    {
    tube::ErrorMessage( "Error writing TRE file: "
      + std::string( err.GetDescription() ) );
    timeCollector.Report();
    return EXIT_FAILURE;
    }

  timeCollector.Stop( "Writing output TRE file" );
  timeCollector.Report();
  return EXIT_SUCCESS;
}

// Main
int main( int argc, char * argv[] )
{
  try
    {
    PARSE_ARGS;
    }
  catch( const std::exception & err )
    {
    tube::ErrorMessage( err.what() );
    return EXIT_FAILURE;
    }
  PARSE_ARGS;

  if( boxCorner.empty() || boxSize.empty() )
    {
    tube::ErrorMessage(
      "Error: longflags --boxCorner and --boxSize are both required");
    return EXIT_FAILURE;
    }

  MetaScene *mScene = new MetaScene;
  mScene->Read( inputTREFile.c_str() );

  if( mScene->GetObjectList()->empty() )
    {
    tubeWarningMacro( << "Input TRE file has no spatial objects" );
    return EXIT_SUCCESS;
    }

  switch( mScene->GetObjectList()->front()->NDims() )
    {
    case 2:
      {
      return DoIt<2>( argc, argv );
      break;
      }

    case 3:
      {
      return DoIt<3>( argc, argv );
      break;
      }

    default:
      {
      tubeErrorMacro(
        << "Error: Only 2D and 3D data is currently supported.");
      return EXIT_FAILURE;
      }
    }
}
<commit_msg>ENH: Remove return/break combo from switch statement - cppcheck warning<commit_after>/*=========================================================================
   Library:   TubeTK

   Copyright 2010 Kitware Inc. 28 Corporate Drive,
   Clifton Park, NY, 12065, USA.

   All rights reserved.

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
=========================================================================*/
#include <iostream>
#include <sstream>

#include "itkTimeProbesCollectorBase.h"
#include "tubeMessage.h"

#include "tubeMacro.h"
#include "metaScene.h"

#include "itkSpatialObjectReader.h"
#include "itkSpatialObjectWriter.h"
#include "itkGroupSpatialObject.h"

#include "ClipTubesCLP.h"

#include <vtkCubeSource.h>
#include <vtkPolyDataWriter.h>
#include <vtkSmartPointer.h>
#include <vtkNew.h>

template< unsigned int DimensionT >
bool isInside( itk::Point< double, DimensionT > pointPos, double tubeRadius,
  itk::Vector< double, DimensionT > boxPos,
  itk::Vector< double, DimensionT > boxSize,
  std::vector<  typename itk::VesselTubeSpatialObjectPoint
    < DimensionT >::CovariantVectorType > normalList )
{
  // Return a boolean indicating if any slice of the tube
  // is included in the box.
  // A slice is considered as a point and an associated radius
  for( unsigned int i = 0; i < normalList.size(); i++ )
    {
    bool hasXInside = false;
    bool hasYInside = false;
    bool hasZInside = false;
    if( pointPos[0] + tubeRadius * normalList[i][0] >= boxPos[0] &&
      pointPos[0] - tubeRadius * normalList[i][0] <= boxPos[0] + boxSize[0] )
      {
      hasXInside = true;
      }
    if( pointPos[1] + tubeRadius * normalList[i][1] >= boxPos[1] &&
      pointPos[1] - tubeRadius * normalList[i][1] <= boxPos[1] + boxSize[1] )
      {
      hasYInside = true;
      }
    switch( DimensionT )
      {
      case 2:
        {
        hasZInside = true;
        break;
        }
      case 3:
        {
        if( pointPos[2] + tubeRadius  * normalList[i][2] >= boxPos[2] &&
          pointPos[2] - tubeRadius * normalList[i][2] <=
            boxPos[2] + boxSize[2] )
          {
          hasZInside = true;
          }
        break;
        }
      default:
        {
        tubeErrorMacro(
          << "Error: Only 2D and 3D data is currently supported." );
        return EXIT_FAILURE;
        }
      }
    if( hasXInside && hasYInside && hasZInside )
      {
      return true;
      }
    }
  return false;
}

template< unsigned int DimensionT >
void writeBox( std::vector< double > boxPos, std::vector< double > boxSize,
  std::vector< double > spacing, std::string boxFileName )
{
  if( DimensionT == 3 )
    {
    vtkSmartPointer<vtkCubeSource> cubeSource =
      vtkSmartPointer<vtkCubeSource>::New();
    cubeSource->SetBounds(
      -spacing[0] * (boxPos[0] + boxSize[0]), -spacing[0] * boxPos[0],
      -spacing[1] * (boxPos[1] + boxSize[1]), -spacing[1] * boxPos[1],
      spacing[2] * boxPos[2], spacing[2] * (boxPos[2] + boxSize[2]) );
    vtkNew<vtkPolyDataWriter> writer;
    writer->SetFileName( boxFileName.c_str() );
    writer->SetInputConnection( cubeSource->GetOutputPort() );
    writer->Write();
    }
  else
    {
    vtkSmartPointer< vtkCubeSource > cubeSource =
      vtkSmartPointer< vtkCubeSource >::New();
    cubeSource->SetBounds(
      -spacing[0] * (boxPos[0] + boxSize[0]), -spacing[0] * boxPos[0],
      -spacing[1] * (boxPos[1] + boxSize[1]), -spacing[1] * boxPos[1],
      0, 0);
    vtkNew<vtkPolyDataWriter> writer;
    writer->SetFileName( boxFileName.c_str() );
    writer->SetInputConnection( cubeSource->GetOutputPort() );
    writer->Write();
    }
}

template< unsigned int DimensionT >
int DoIt (int argc, char * argv[])
{
  PARSE_ARGS;

  // Ensure that the input image dimension is valid
  // We only support 2D and 3D Images due to the
  // limitation of itkTubeSpatialObject
  if( DimensionT != 2 && DimensionT != 3 )
    {
    tube::ErrorMessage(
      "Error: Only 2D and 3D data is currently supported.");
    return EXIT_FAILURE;
    }

  // The timeCollector to perform basic profiling of algorithmic components
  itk::TimeProbesCollectorBase timeCollector;

  // Load TRE File
  tubeStandardOutputMacro( << "\n>> Loading TRE File" );

  typedef itk::SpatialObjectReader< DimensionT >          TubesReaderType;
  typedef itk::GroupSpatialObject< DimensionT >           TubeGroupType;
  typedef itk::VesselTubeSpatialObject< DimensionT >      TubeType;
  typedef itk::VesselTubeSpatialObjectPoint< DimensionT > TubePointType;

  timeCollector.Start( "Loading Input TRE File" );

  typename TubesReaderType::Pointer tubeFileReader = TubesReaderType::New();

  try
    {
    tubeFileReader->SetFileName( inputTREFile.c_str() );
    tubeFileReader->Update();
    }
  catch( itk::ExceptionObject & err )
    {
    tube::ErrorMessage( "Error loading TRE File: "
      + std::string( err.GetDescription() ) );
    timeCollector.Report();
    return EXIT_FAILURE;
    }
  //Cast XML vector parameters
  typedef itk::Vector< double, DimensionT > VectorType;
  VectorType boxPositionVector;
  VectorType boxSizeVector;
  for( unsigned int i = 0; i < DimensionT; i++ )
    {
    boxPositionVector[i] = boxCorner[i];
    boxSizeVector[i] = boxSize[i];
    }

  typename TubeGroupType::Pointer pSourceTubeGroup =
    tubeFileReader->GetGroup();
  typename TubeGroupType::ChildrenListPointer pSourceTubeList =
    pSourceTubeGroup->GetChildren();

  timeCollector.Stop( "Loading Input TRE File" );

  // Compute clipping
  tubeStandardOutputMacro( << "\n>> Finding Tubes for Clipping" );

  timeCollector.Start( "Selecting Tubes" );
  //Target Group to save desired tubes
  typename TubeGroupType::Pointer pTargetTubeGroup = TubeGroupType::New();

  pTargetTubeGroup->CopyInformation( pSourceTubeGroup );
  // TODO: make CopyInformation of itk::SpatialObject do this
  pTargetTubeGroup->GetObjectToParentTransform()->SetScale(
    pSourceTubeGroup->GetObjectToParentTransform()->GetScale() );
  pTargetTubeGroup->GetObjectToParentTransform()->SetOffset(
    pSourceTubeGroup->GetObjectToParentTransform()->GetOffset() );
  pTargetTubeGroup->GetObjectToParentTransform()->SetMatrix(
    pSourceTubeGroup->GetObjectToParentTransform()->GetMatrix() );
  pTargetTubeGroup->SetSpacing( pSourceTubeGroup->GetSpacing() );
  pTargetTubeGroup->ComputeObjectToWorldTransform();

  int targetTubeId=0;
  std::vector< double > spacing( DimensionT, 0 );

  for( typename TubeGroupType::ChildrenListType::iterator
    tubeList_it = pSourceTubeList->begin();
    tubeList_it != pSourceTubeList->end(); ++tubeList_it)
    {
    //**** Source Tube **** :
    typename TubeType::Pointer pCurSourceTube =
      dynamic_cast< TubeType* >( tubeList_it->GetPointer() );
    //dynamic_cast verification
    if(!pCurSourceTube)
      {
      return EXIT_FAILURE;
      }
    //Compute Tangent and Normals
    pCurSourceTube->ComputeTangentAndNormals();
    //pCurSourceTube->ComputeObjectToWorldTransform();//BUG
    //Point List for TargetTube
    typename TubeType::PointListType TargetPointList;
    //Get points in current source tube
    typename TubeType::PointListType pointList =
      pCurSourceTube->GetPoints();

    //Get Index to World Transformation
    typename TubeType::TransformType * pTubeIndexPhysTransform =
      pCurSourceTube->GetIndexToWorldTransform();

    for( typename TubeType::PointListType::const_iterator
      pointList_it = pointList.begin();
      pointList_it != pointList.end(); ++pointList_it )
      {
      TubePointType curSourcePoint = *pointList_it;
      //Transform parameters in physical space
      typename TubePointType::PointType curSourcePos =
        pTubeIndexPhysTransform->TransformPoint(
          curSourcePoint.GetPosition() );
      typename TubePointType::VectorType worldBoxposition =
        pTubeIndexPhysTransform->TransformVector( boxPositionVector );
      typename TubePointType::VectorType worldBoxSize =
        pTubeIndexPhysTransform->TransformVector( boxSizeVector );
      typename TubePointType::CovariantVectorType curTubeNormal1 =
        pTubeIndexPhysTransform->TransformCovariantVector(
          curSourcePoint.GetNormal1() );
      typename TubePointType::CovariantVectorType curTubeNormal2 =
        pTubeIndexPhysTransform->TransformCovariantVector(
          curSourcePoint.GetNormal2() );
      //Save Normals in a vector to pass it as an argument for IsIside()
      std::vector<typename TubePointType::CovariantVectorType> normalList;
      normalList.push_back(curTubeNormal1);
      if( DimensionT == 3 )
        {
        normalList.push_back(curTubeNormal2);
        }
      //Save point in target tube if it belongs to the box
      if( isInside( curSourcePos, curSourcePoint.GetRadius(),
        worldBoxposition, worldBoxSize, normalList ) )
        {
        if( ClipTubes )
          {
          TargetPointList.push_back( curSourcePoint );
          }
        else
          {
          pCurSourceTube->SetId( targetTubeId );
          ++targetTubeId;
          pTargetTubeGroup->AddSpatialObject( pCurSourceTube );
          break;
          }
        }
      else
        {
        if( TargetPointList.size() > 0 )
          {
          //**** Target Tube **** :
          typename TubeType::Pointer pTargetTube = TubeType::New();

          pTargetTube->CopyInformation( pCurSourceTube );

          // TODO: make CopyInformation of itk::SpatialObject do this
          pTargetTube->GetObjectToParentTransform()->SetScale(
            pCurSourceTube->GetObjectToParentTransform()->GetScale() );
          pTargetTube->GetObjectToParentTransform()->SetOffset(
            pCurSourceTube->GetObjectToParentTransform()->GetOffset() );
          pTargetTube->GetObjectToParentTransform()->SetMatrix(
            pCurSourceTube->GetObjectToParentTransform()->GetMatrix() );
          pTargetTube->SetSpacing( pCurSourceTube->GetSpacing() );
          pTargetTube->ComputeObjectToWorldTransform();

          pTargetTube->ComputeTangentAndNormals();

          pTargetTube->SetId( targetTubeId );
          ++targetTubeId;
          //Save clipped tube
          pTargetTube->SetPoints( TargetPointList );
          pTargetTubeGroup->AddSpatialObject( pTargetTube );

          TargetPointList.clear();
          }
        }
      }
    if( TargetPointList.size() > 0 )
      {
      //**** Target Tube **** :
      typename TubeType::Pointer pTargetTube = TubeType::New();

      pTargetTube->CopyInformation( pCurSourceTube );

      // TODO: make CopyInformation of itk::SpatialObject do this
      pTargetTube->GetObjectToParentTransform()->SetScale(
        pCurSourceTube->GetObjectToParentTransform()->GetScale() );
      pTargetTube->GetObjectToParentTransform()->SetOffset(
        pCurSourceTube->GetObjectToParentTransform()->GetOffset() );
      pTargetTube->GetObjectToParentTransform()->SetMatrix(
        pCurSourceTube->GetObjectToParentTransform()->GetMatrix() );
      pTargetTube->SetSpacing( pCurSourceTube->GetSpacing() );
      pTargetTube->ComputeObjectToWorldTransform();

      pTargetTube->ComputeTangentAndNormals();

      pTargetTube->SetId( targetTubeId );
      ++targetTubeId;
      //Save clipped tube
      pTargetTube->SetPoints( TargetPointList );
      pTargetTubeGroup->AddSpatialObject( pTargetTube );

      TargetPointList.clear();
      }
    for( unsigned int d=0; d<DimensionT; ++d )
      {
      spacing[d] = pCurSourceTube->GetSpacing()[d];
      }
    }
  if( !outputBoxFile.empty() )
    {
    writeBox<DimensionT>( boxCorner, boxSize, spacing, outputBoxFile );
    }

  timeCollector.Stop( "Selecting Tubes" );

  // Write output TRE file
  tubeStandardOutputMacro(
    << "\n>> Writing TRE file" );

  timeCollector.Start( "Writing output TRE file" );

  typedef itk::SpatialObjectWriter< DimensionT > TubeWriterType;
  typename TubeWriterType::Pointer tubeWriter = TubeWriterType::New();

  try
    {
    tubeWriter->SetFileName( outputTREFile.c_str() );
    tubeWriter->SetInput(pTargetTubeGroup);
    tubeWriter->Update();
    }
  catch( itk::ExceptionObject & err )
    {
    tube::ErrorMessage( "Error writing TRE file: "
      + std::string( err.GetDescription() ) );
    timeCollector.Report();
    return EXIT_FAILURE;
    }

  timeCollector.Stop( "Writing output TRE file" );
  timeCollector.Report();
  return EXIT_SUCCESS;
}

// Main
int main( int argc, char * argv[] )
{
  try
    {
    PARSE_ARGS;
    }
  catch( const std::exception & err )
    {
    tube::ErrorMessage( err.what() );
    return EXIT_FAILURE;
    }
  PARSE_ARGS;

  if( boxCorner.empty() || boxSize.empty() )
    {
    tube::ErrorMessage(
      "Error: longflags --boxCorner and --boxSize are both required");
    return EXIT_FAILURE;
    }

  MetaScene *mScene = new MetaScene;
  mScene->Read( inputTREFile.c_str() );

  if( mScene->GetObjectList()->empty() )
    {
    tubeWarningMacro( << "Input TRE file has no spatial objects" );
    return EXIT_SUCCESS;
    }

  switch( mScene->GetObjectList()->front()->NDims() )
    {
    case 2:
      {
      return DoIt<2>( argc, argv );
      }

    case 3:
      {
      return DoIt<3>( argc, argv );
      }

    default:
      {
      tubeErrorMacro(
        << "Error: Only 2D and 3D data is currently supported.");
      return EXIT_FAILURE;
      }
    }
}
<|endoftext|>


<commit_before>#include "ruby.h"

#include <re2/re2.h>

#include <string>
#include <new>
using std::nothrow;

using re2::RE2;
using re2::StringPiece;

static VALUE rb_cRRE2, rb_cRRE2MatchData;

/* RRE2MatchData Class */
struct rre2_matchdata {
	StringPiece *groups;
	int n_groups;
};

extern "C" static void
rre2_matchdata_free(rre2_matchdata *m_obj)
{
	if (m_obj) {
		if (m_obj->groups)
			delete[] m_obj->groups;
		free(m_obj);
	}
}

extern "C" static VALUE
rre2_matchdata_alloc(VALUE klass)
{
	rre2_matchdata *m_obj;
	return Data_Make_Struct(klass, struct rre2_matchdata, 0, rre2_matchdata_free, m_obj);
}

extern "C" static VALUE
rre2_matchdata_to_s(VALUE self)
{
	int i;
	struct rre2_matchdata *m_obj = (struct rre2_matchdata *)DATA_PTR(self);
//	printf("matchdata_to_s: %x\n", m_obj);
	for (i = 0; i < m_obj->n_groups; i++) {
		if (m_obj->groups[i] != NULL) {
//			printf("match[%d]: %s\n", i, m_obj->groups[i].data());
			return rb_str_new2(m_obj->groups[i].as_string().data());
		}
	}
	return Qnil;
}

extern "C" static VALUE
rre2_matchdata_to_a(VALUE self)
{
	VALUE ary;
	int i;
	struct rre2_matchdata *m_obj = (struct rre2_matchdata *)DATA_PTR(self);
//	printf("matchdata_to_s: %x\n", m_obj);
	ary = rb_ary_new();
	for (i = 0; i < m_obj->n_groups; i++) {
		if (m_obj->groups[i] != NULL) {
//			printf("match[%d]: %s\n", i, m_obj->groups[i].data());
			rb_ary_push(ary, rb_str_new2(m_obj->groups[i].as_string().data()));
		}
	}
	return ary;
}

/* RRE2 Class */
extern "C" static void
rre2_free(RE2 *re_obj)
{
	if (re_obj)
		delete re_obj;
}

extern "C" static VALUE
rre2_alloc(VALUE klass)
{
	RE2* re2_obj = NULL;
	return Data_Wrap_Struct(klass, NULL, rre2_free, re2_obj);
}

extern "C" static VALUE
rre2_init(VALUE self, VALUE rpattern)
{
	RE2 *re_obj;

#if 0
	Check_Type(rpattern, T_STRING);
	printf("rre2_init: ruby pattern=%s\n", RSTRING(rpattern)->ptr);
	DATA_PTR(self) = re_obj = new(nothrow) RE2(StringPiece(RSTRING(rpattern)->ptr, RSTRING(rpattern)->len));
#else
	char *pattern = StringValuePtr(rpattern);
//	printf("rre2_init: ruby pattern=%s\n", pattern);
	DATA_PTR(self) = re_obj = new(nothrow) RE2(StringPiece(pattern, strlen(pattern)));
#endif

	if (re_obj == NULL)
		rb_raise(rb_eRuntimeError, "failed to initialize RE2");

	if (!re_obj->ok()) {
		long code = (long)re_obj->error_code();
		const std::string& msg = re_obj->error();
		rb_raise(rb_eRuntimeError, "failed to initialize RE2: %d:%s", code, msg.data());
	}

	return self;
}

extern "C" static VALUE
rre2_match(int argc, VALUE *argv, VALUE self)
{
	RE2 *re_obj = (RE2 *)DATA_PTR(self);
	struct rre2_matchdata *m_obj;
	VALUE result, str, initpos;
	long pos;
	char *data;
	int ret, n_groups = 0;
	StringPiece *groups = NULL;

	if (rb_scan_args(argc, argv, "11", &str, &initpos) == 2) {
		pos = NUM2LONG(initpos);
	} else {
		pos = 0;
	}

    n_groups = re_obj->NumberOfCapturingGroups() + 1;
    groups = new(nothrow) StringPiece[n_groups];

	if (groups == NULL)
		rb_raise(rb_eNoMemError, "failed to init RRE2MatchData");

	// FIXME: should not be using StringValuePtr
	data = StringValuePtr(str);
	if (pos >= strlen(data))
		rb_raise(rb_eRuntimeError, "invalid position specified");

//	printf("Running match on: %s\n", StringPiece(data+pos, strlen(data)-pos).data());

	// FIXME: support different kind of matching
	ret = re_obj->Match(
		StringPiece(data+pos, strlen(data)-pos),
		0,
		RE2::UNANCHORED,
		groups,
		n_groups
	);

//	printf("Match returned: %d\n", ret);

	if (ret != 1) {
		delete[] groups;
		return Qnil;
	}

	// FIXME: check return value?
    result = rre2_matchdata_alloc(rb_cRRE2MatchData); //reg_match_pos(re, &str, pos);

	m_obj = (struct rre2_matchdata *)DATA_PTR(result);
	m_obj->groups = groups;
	m_obj->n_groups = n_groups;

	if (!NIL_P(result) && rb_block_given_p()) {
		return rb_yield(result);
	}

	return result;
}

extern "C" static VALUE
rre2_inspect(VALUE self)
{
	RE2 *re_obj = (RE2 *)DATA_PTR(self);
	const std::string& msg = re_obj->pattern();
	return rb_str_new2(msg.data());
}

extern "C" static VALUE
rre2_program_size(VALUE self)
{
	RE2 *re_obj = (RE2 *)DATA_PTR(self);
	return rb_int_new(re_obj->ProgramSize());
}

extern "C" void
Init_rre2()
{
	rb_cRRE2 = rb_define_class("RRE2", rb_cObject);
	rb_define_alloc_func(rb_cRRE2, rre2_alloc);
	rb_define_method(rb_cRRE2, "initialize", (VALUE (*)(...))rre2_init, 1);
    rb_define_method(rb_cRRE2, "match", (VALUE (*)(...))rre2_match, -1);
    rb_define_method(rb_cRRE2, "to_s", (VALUE (*)(...))rre2_inspect, 0);
    rb_define_method(rb_cRRE2, "inspect", (VALUE (*)(...))rre2_inspect, 0);
    rb_define_method(rb_cRRE2, "source", (VALUE (*)(...))rre2_inspect, 0);

	rb_define_method(rb_cRRE2, "program_size", (VALUE (*)(...))rre2_program_size, 0);

	rb_cRRE2MatchData = rb_define_class("RRE2MatchData", rb_cObject);
	rb_define_alloc_func(rb_cRRE2MatchData, rre2_matchdata_alloc);
	rb_undef_method(CLASS_OF(rb_cRRE2MatchData), "new");

	rb_define_method(rb_cRRE2MatchData, "to_s", (VALUE (*)(...))rre2_matchdata_to_s, 0);
	rb_define_method(rb_cRRE2MatchData, "to_a", (VALUE (*)(...))rre2_matchdata_to_a, 0);
};
<commit_msg>Export escape/quote<commit_after>#include "ruby.h"

#include <re2/re2.h>

#include <string>
#include <new>
using std::nothrow;

using re2::RE2;
using re2::StringPiece;

static VALUE rb_cRRE2, rb_cRRE2MatchData;

/* RRE2MatchData Class */
struct rre2_matchdata {
	StringPiece *groups;
	int n_groups;
};

extern "C" static void
rre2_matchdata_free(rre2_matchdata *m_obj)
{
	if (m_obj) {
		if (m_obj->groups)
			delete[] m_obj->groups;
		free(m_obj);
	}
}

extern "C" static VALUE
rre2_matchdata_alloc(VALUE klass)
{
	rre2_matchdata *m_obj;
	return Data_Make_Struct(klass, struct rre2_matchdata, 0, rre2_matchdata_free, m_obj);
}

extern "C" static VALUE
rre2_matchdata_to_s(VALUE self)
{
	int i;
	struct rre2_matchdata *m_obj = (struct rre2_matchdata *)DATA_PTR(self);
//	printf("matchdata_to_s: %x\n", m_obj);
	for (i = 0; i < m_obj->n_groups; i++) {
		if (m_obj->groups[i] != NULL) {
//			printf("match[%d]: %s\n", i, m_obj->groups[i].data());
			return rb_str_new2(m_obj->groups[i].as_string().data());
		}
	}
	return Qnil;
}

extern "C" static VALUE
rre2_matchdata_to_a(VALUE self)
{
	VALUE ary;
	int i;
	struct rre2_matchdata *m_obj = (struct rre2_matchdata *)DATA_PTR(self);
//	printf("matchdata_to_s: %x\n", m_obj);
	ary = rb_ary_new();
	for (i = 0; i < m_obj->n_groups; i++) {
		if (m_obj->groups[i] != NULL) {
//			printf("match[%d]: %s\n", i, m_obj->groups[i].data());
			rb_ary_push(ary, rb_str_new2(m_obj->groups[i].as_string().data()));
		}
	}
	return ary;
}

/* RRE2 Class */
extern "C" static void
rre2_free(RE2 *re_obj)
{
	if (re_obj)
		delete re_obj;
}

extern "C" static VALUE
rre2_alloc(VALUE klass)
{
	RE2* re2_obj = NULL;
	return Data_Wrap_Struct(klass, NULL, rre2_free, re2_obj);
}

extern "C" static VALUE
rre2_init(VALUE self, VALUE rpattern)
{
	RE2 *re_obj;

#if 0
	Check_Type(rpattern, T_STRING);
	printf("rre2_init: ruby pattern=%s\n", RSTRING(rpattern)->ptr);
	DATA_PTR(self) = re_obj = new(nothrow) RE2(StringPiece(RSTRING(rpattern)->ptr, RSTRING(rpattern)->len));
#else
	char *pattern = StringValuePtr(rpattern);
//	printf("rre2_init: ruby pattern=%s\n", pattern);
	DATA_PTR(self) = re_obj = new(nothrow) RE2(StringPiece(pattern, strlen(pattern)));
#endif

	if (re_obj == NULL)
		rb_raise(rb_eRuntimeError, "failed to initialize RE2");

	if (!re_obj->ok()) {
		long code = (long)re_obj->error_code();
		const std::string& msg = re_obj->error();
		rb_raise(rb_eRuntimeError, "failed to initialize RE2: %d:%s", code, msg.data());
	}

	return self;
}

extern "C" static VALUE
rre2_match(int argc, VALUE *argv, VALUE self)
{
	RE2 *re_obj = (RE2 *)DATA_PTR(self);
	struct rre2_matchdata *m_obj;
	VALUE result, str, initpos;
	long pos;
	char *data;
	int ret, n_groups = 0;
	StringPiece *groups = NULL;

	if (rb_scan_args(argc, argv, "11", &str, &initpos) == 2) {
		pos = NUM2LONG(initpos);
	} else {
		pos = 0;
	}

    n_groups = re_obj->NumberOfCapturingGroups() + 1;
    groups = new(nothrow) StringPiece[n_groups];

	if (groups == NULL)
		rb_raise(rb_eNoMemError, "failed to init RRE2MatchData");

	// FIXME: should not be using StringValuePtr
	data = StringValuePtr(str);
	if (pos >= strlen(data))
		rb_raise(rb_eRuntimeError, "invalid position specified");

//	printf("Running match on: %s\n", StringPiece(data+pos, strlen(data)-pos).data());

	// FIXME: support different kind of matching
	ret = re_obj->Match(
		StringPiece(data+pos, strlen(data)-pos),
		0,
		RE2::UNANCHORED,
		groups,
		n_groups
	);

//	printf("Match returned: %d\n", ret);

	if (ret != 1) {
		delete[] groups;
		return Qnil;
	}

	// FIXME: check return value?
    result = rre2_matchdata_alloc(rb_cRRE2MatchData); //reg_match_pos(re, &str, pos);

	m_obj = (struct rre2_matchdata *)DATA_PTR(result);
	m_obj->groups = groups;
	m_obj->n_groups = n_groups;

	if (!NIL_P(result) && rb_block_given_p()) {
		return rb_yield(result);
	}

	return result;
}

extern "C" static VALUE
rre2_inspect(VALUE self)
{
	RE2 *re_obj = (RE2 *)DATA_PTR(self);
	const std::string& msg = re_obj->pattern();
	return rb_str_new2(msg.data());
}

extern "C" static VALUE
rre2_escape(VALUE self, VALUE rpattern)
{
	char *pattern = StringValuePtr(rpattern);
	return rb_str_new2(RE2::QuoteMeta(pattern).data());
}

extern "C" static VALUE
rre2_program_size(VALUE self)
{
	RE2 *re_obj = (RE2 *)DATA_PTR(self);
	return rb_int_new(re_obj->ProgramSize());
}

extern "C" void
Init_rre2()
{
	rb_cRRE2 = rb_define_class("RRE2", rb_cObject);
	rb_define_alloc_func(rb_cRRE2, rre2_alloc);
	rb_define_method(rb_cRRE2, "initialize", (VALUE (*)(...))rre2_init, 1);
    rb_define_method(rb_cRRE2, "match", (VALUE (*)(...))rre2_match, -1);
    rb_define_method(rb_cRRE2, "to_s", (VALUE (*)(...))rre2_inspect, 0);
    rb_define_method(rb_cRRE2, "inspect", (VALUE (*)(...))rre2_inspect, 0);
    rb_define_method(rb_cRRE2, "source", (VALUE (*)(...))rre2_inspect, 0);

	rb_define_singleton_method(rb_cRRE2, "escape", (VALUE (*)(...))rre2_escape, 1);
	rb_define_singleton_method(rb_cRRE2, "quote", (VALUE (*)(...))rre2_escape, 1);

	rb_define_method(rb_cRRE2, "program_size", (VALUE (*)(...))rre2_program_size, 0);

	rb_cRRE2MatchData = rb_define_class("RRE2MatchData", rb_cObject);
	rb_define_alloc_func(rb_cRRE2MatchData, rre2_matchdata_alloc);
	rb_undef_method(CLASS_OF(rb_cRRE2MatchData), "new");

	rb_define_method(rb_cRRE2MatchData, "to_s", (VALUE (*)(...))rre2_matchdata_to_s, 0);
	rb_define_method(rb_cRRE2MatchData, "to_a", (VALUE (*)(...))rre2_matchdata_to_a, 0);
};
<|endoftext|>


<commit_before>#include <QtTest/QtTest>
#include <QVariant>
#include <QMetaType>

#include "response.h"
#include "handler.h"
#include "handler/list.h"
#include "mockobjects.h"

using namespace Akonadi;

class TestHandler: public QObject {
  Q_OBJECT
private slots:

    void initTestCase()
    {
        qRegisterMetaType<Response>("Response");
    }

    void testInit()
    {

    }

    /// ---- List ----

    void testSeparatorList()
    {
        Handler* l = getHandlerFor("LIST");
        QVERIFY( dynamic_cast<List*>(l) != 0 );

        const QByteArray line = "1 LIST \"\" \"\"";

        QSignalSpy spy(l, SIGNAL( responseAvailable( const Response& )));
        l->handleLine( line );
        QCOMPARE(spy.count(), 2);

        const QString expectedFirstResponse = "* LIST (\\Noselect) \"/\" \"\"";
        QVERIFY(nextResponse(spy).asString() == expectedFirstResponse);

        const QString expectedSecondResponse = "1 OK List completed";
        QVERIFY(nextResponse(spy).asString() == expectedSecondResponse);
    }

    void testRootPercentList()
    {
        Handler* l = getHandlerFor("LIST");
        QVERIFY( dynamic_cast<List*>(l) != 0 );

        const QByteArray line = "1 LIST \"\" \"%\"";

        QSignalSpy spy(l, SIGNAL( responseAvailable( const Response& )));
        l->handleLine( line );
        QCOMPARE(spy.count(), 2);

        const QByteArray expectedFirstResponse = "* LIST () \"/\" \"INBOX\"";
        QCOMPARE(nextResponse(spy).asString(), expectedFirstResponse );

        const QByteArray expectedSecondResponse = "1 OK List completed";
        QCOMPARE(nextResponse(spy).asString(), expectedSecondResponse );
    }

    void testRootStarList()
    {
        Handler* l = getHandlerFor("LIST");
        QVERIFY( dynamic_cast<List*>(l) != 0 );

        const QByteArray line = "1 LIST \"\" \"*\"";

        QSignalSpy spy(l, SIGNAL( responseAvailable( const Response& )));
        l->handleLine( line );
        QCOMPARE(spy.count(), 3);

        const QByteArray expectedFirstResponse = "* LIST () \"/\" \"INBOX\"";
        QCOMPARE(nextResponse(spy).asString(), expectedFirstResponse );

        const QByteArray expectedSecondResponse = "* LIST () \"/\" \"INBOX/foo\"";
        QCOMPARE(nextResponse(spy).asString(), expectedSecondResponse );

        const QByteArray expectedThirdResponse = "1 OK List completed";
        QCOMPARE(nextResponse(spy).asString(), expectedThirdResponse );
    }

    void testInboxList()
    {
        Handler* l = getHandlerFor("LIST");
        QVERIFY( dynamic_cast<List*>(l) != 0 );

        const QByteArray line = "1 LIST \"\" \"INBOX\"";

        QSignalSpy spy(l, SIGNAL( responseAvailable( const Response& )));
        l->handleLine( line );
        QCOMPARE(spy.count(), 3);

        const QByteArray expectedFirstResponse = "* LIST () \"/\" \"foo\"";
        QCOMPARE(nextResponse(spy).asString(), expectedFirstResponse );

        const QByteArray expectedSecondResponse = "* LIST () \"/\" \"bar\"";
        QCOMPARE(nextResponse(spy).asString(), expectedSecondResponse );

        const QByteArray expectedThirdResponse = "1 OK List completed";
        QCOMPARE(nextResponse(spy).asString(), expectedThirdResponse );
    }

private:
    // Helper
    Response nextResponse( QSignalSpy& spy )
    {
        QList<QVariant> arguments = spy.takeFirst();
        Response r = qvariant_cast<Response>(arguments.at(0));
        //qDebug() << "Response: " << r.asString();
        return r;
    }

    Handler* getHandlerFor(const QByteArray& command )
    {
        Handler *h = Handler::findHandlerForCommandAuthenticated( command );
        if( h != 0 ) {
            h->setTag("1");
            h->setConnection( MockObjects::mockConnection() );
        }
        return h;
    }

};
Q_DECLARE_METATYPE(Response)
QTEST_MAIN(TestHandler)

#include "main.moc"
<commit_msg>Testing is good.<commit_after>#include <QtTest/QtTest>
#include <QVariant>
#include <QMetaType>

#include "response.h"
#include "handler.h"
#include "handler/list.h"
#include "mockobjects.h"

using namespace Akonadi;

class TestHandler: public QObject {
  Q_OBJECT
private slots:

    void initTestCase()
    {
        qRegisterMetaType<Response>("Response");
    }

    void testInit()
    {
    }

    /// ---- List ----

    void testSeparatorList()
    {
        Handler* l = getHandlerFor("LIST");
        QVERIFY( dynamic_cast<List*>(l) != 0 );

        const QByteArray line = "1 LIST \"\" \"\"";

        QSignalSpy spy(l, SIGNAL( responseAvailable( const Response& )));
        l->handleLine( line );
        QCOMPARE(spy.count(), 2);

        const QString expectedFirstResponse = "* LIST (\\Noselect) \"/\" \"\"";
        QVERIFY(nextResponse(spy).asString() == expectedFirstResponse);

        const QString expectedSecondResponse = "1 OK List completed";
        QVERIFY(nextResponse(spy).asString() == expectedSecondResponse);
    }

    void testRootPercentList()
    {
        Handler* l = getHandlerFor("LIST");
        QVERIFY( dynamic_cast<List*>(l) != 0 );

        const QByteArray line = "1 LIST \"\" \"%\"";

        QSignalSpy spy(l, SIGNAL( responseAvailable( const Response& )));
        l->handleLine( line );
        QCOMPARE(spy.count(), 2);

        const QByteArray expectedFirstResponse = "* LIST () \"/\" \"INBOX\"";
        QCOMPARE(nextResponse(spy).asString(), expectedFirstResponse );

        const QByteArray expectedSecondResponse = "1 OK List completed";
        QCOMPARE(nextResponse(spy).asString(), expectedSecondResponse );
    }

    void testRootStarList()
    {
        Handler* l = getHandlerFor("LIST");
        QVERIFY( dynamic_cast<List*>(l) != 0 );

        const QByteArray line = "1 LIST \"\" \"*\"";

        QSignalSpy spy(l, SIGNAL( responseAvailable( const Response& )));
        l->handleLine( line );
        QCOMPARE(spy.count(), 3);

        const QByteArray expectedFirstResponse = "* LIST () \"/\" \"INBOX\"";
        QCOMPARE(nextResponse(spy).asString(), expectedFirstResponse );

        const QByteArray expectedSecondResponse = "* LIST () \"/\" \"INBOX/foo\"";
        QCOMPARE(nextResponse(spy).asString(), expectedSecondResponse );

        const QByteArray expectedThirdResponse = "1 OK List completed";
        QCOMPARE(nextResponse(spy).asString(), expectedThirdResponse );
    }

    void testInboxList()
    {
        Handler* l = getHandlerFor("LIST");
        QVERIFY( dynamic_cast<List*>(l) != 0 );

        const QByteArray line = "1 LIST \"\" \"INBOX\"";

        QSignalSpy spy(l, SIGNAL( responseAvailable( const Response& )));
        l->handleLine( line );
        QCOMPARE(spy.count(), 3);

        const QByteArray expectedFirstResponse = "* LIST () \"/\" \"foo\"";
        QCOMPARE(nextResponse(spy).asString(), expectedFirstResponse );

        const QByteArray expectedSecondResponse = "* LIST () \"/\" \"bar\"";
        QCOMPARE(nextResponse(spy).asString(), expectedSecondResponse );

        const QByteArray expectedThirdResponse = "1 OK List completed";
        QCOMPARE(nextResponse(spy).asString(), expectedThirdResponse );
    }

    /// ---- Fetch ----

    void testFetch()
    {

    }

private:
    // Helper
    Response nextResponse( QSignalSpy& spy )
    {
        QList<QVariant> arguments = spy.takeFirst();
        Response r = qvariant_cast<Response>(arguments.at(0));
        //qDebug() << "Response: " << r.asString();
        return r;
    }

    Handler* getHandlerFor(const QByteArray& command )
    {
        Handler *h = Handler::findHandlerForCommandAuthenticated( command );
        if( h != 0 ) {
            h->setTag("1");
            h->setConnection( MockObjects::mockConnection() );
        }
        return h;
    }

};
Q_DECLARE_METATYPE(Response)
QTEST_MAIN(TestHandler)

#include "main.moc"
<|endoftext|>


<commit_before>#include <string>
#include <vector>
#include <iostream>
#include <fstream>
#include <memory>

using namespace std;

namespace aihw2 {

// Inverse graph,
// Directed connections are stored as references
// to parents instead of children.
class Node {
public:
  Node(string n) : _name(n) {}
  void addParent(shared_ptr<Node> p) {
    parents.push_back(p);
  }
  string name() const { return _name; }
  long double value() const { return _value; }
  void set_value(long double val) { _value = val; }
private:
  string _name;
  long double _value = 0;
  vector<shared_ptr<Node>> parents;
};

ostream& operator<<(ostream& os, const Node& node) {
  os << "node(" << node.name() << "): " << node.value();
  return os;
}

class Network {
public:
  void addNode(shared_ptr<Node> n) {
    nodes.push_back(n);
  }
private:
  vector<shared_ptr<Node>> nodes;
};

class Homework {
public:
  static void Main(vector<string> args) {
    // Crappy CSV parsing
    if (args.size() != 2) {
      cout << "usage: " << args[0] << " survey-file" << endl;
      return;
    }
    auto input = ifstream(args[1]);
    auto gn = shared_ptr<Node>(new Node("G")),
      hn = shared_ptr<Node>(new Node("H")),
      tn = shared_ptr<Node>(new Node("T")),
      mn = shared_ptr<Node>(new Node("M")),
      pn = shared_ptr<Node>(new Node("P")),
      cn = shared_ptr<Node>(new Node("C"));
    cn->addParent(gn);
    cn->addParent(pn);
    cn->addParent(mn);
    pn->addParent(hn);
    pn->addParent(tn);
    vector<shared_ptr<Node>> vec = { gn, hn, tn, mn, pn, cn };

    int lines;
    for (lines = 0; !input.eof(); lines++) {
      for (auto i = 0; i < vec.size(); i++) {
        int j;
        input >> j;
        vec[i]->set_value(vec[i]->value() + j);
      }
    }
    for (n : vec) {
      n->set_value(n->value()/lines);
    }
    cout << "Got the following values" << endl;
    for (n : vec) cout << *n << endl;

    // Create Network.
    auto net = new Network();
    for (n : vec) net->addNode(n);
  }
};

} // namespace aihw2

// Program entry point
int main(int argc, const char *argv[]) {
  vector<string> vec;
  for (auto i = 0; i < argc; i++) {
    vec.push_back(std::string(argv[i]));
  }
  aihw2::Homework::Main(vec);
}
<commit_msg>update<commit_after>#include <string>
#include <vector>
#include <iostream>
#include <fstream>
#include <memory>
#include <map>

using namespace std;

namespace aihw2 {

class Homework {
public:
  static void Main(vector<string> args) {
    // Crappy CSV parsing
    if (args.size() != 2) {
      cout << "usage: " << args[0] << " survey-file" << endl;
      return;
    }
    auto input = ifstream(args[1]);
    vector<int> vec; // g,h,t,m,p,c

    int lines;
    for (lines = 0; !input.eof(); lines++) {
      int entry = 0;
      char l[100];
      input.getline(l, 100);
      for (auto i = 0; i < 6; i++) {
        char c = l[(i*2)];
        int j;
        if (c == '1') j = 1;
        if (c == '0') j = 0;
        entry |= j << (5-i);
      }
      vec.push_back(entry);
    }

    vector<long double> c(8);
    vector<int> cc(8);
    vector<long double> p(4);
    vector<int> cp(8);
    vector<int> all(6);
    vector<int> call(6);
    for (auto n : vec) {
      for (int i = 0; i < 6; i++) {
        all[i] += (n & (1 << i)) >> i;
        call[i]++;
        int j, val;
        // j is c's parents
        j = ((n & (1 << 5)) >> 5) << 2 | ((n & (1 << 2)) >> 2) << 1 | ((n & (1 << 1)) >> 1);
        val = n & 1;
        c[j] += val;
        cc[j]++;
        // j is p's parents
        j = ((n & (1 << 4)) >> 4) << 1 | ((n & (1 << 3)) >> 3);
        val = (n & (1 << 1)) >> 1;
        p[j] += val;
        cp[j]++;
      }
    }
    for (int i = 0; i < c.size(); i++) c[i]/=cc[i];
    for (int i = 0; i < p.size(); i++) p[i]/=cp[i];

    // Proportion true on each
    for (int i = 0; i < 6; i++) {
      all[i] /= call[i];
    }

    long double sum = 0;
    for (int m = 0; m < 2; m++) {
      for (int g = 0; g < 2; g++) {
        for (int t = 0; t < 2; t++) {
          for (int pn = 0; pn < 2; pn++) {
            int h = 1;
            long double pm = m ? all[2] : 1-all[2],
              pg = g ? all[5] : 1-all[5],
              pt = t ? all[3] : 1-all[3],
              pp = pn ? all[1] : 1-all[1],
              pc = c[g << 2 | m << 1 | pn],
              ppp = p[h << 1 | t];
            sum += pm*pg*pt*pp*pc*ppp;
          }
        }
      }
    }
    cout << "sum: " << sum << endl;
  }
};

} // namespace aihw2

// Program entry point
int main(int argc, const char *argv[]) {
  vector<string> vec;
  for (auto i = 0; i < argc; i++) {
    vec.push_back(std::string(argv[i]));
  }
  aihw2::Homework::Main(vec);
}
<|endoftext|>


<commit_before>/*
** Copyright 2009-2014 Merethis
**
** This file is part of Centreon Broker.
**
** Centreon Broker is free software: you can redistribute it and/or
** modify it under the terms of the GNU General Public License version 2
** as published by the Free Software Foundation.
**
** Centreon Broker is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
** General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with Centreon Broker. If not, see
** <http://www.gnu.org/licenses/>.
*/
#ifndef CCBN_BOOL_EXPRESSION_HH
# define CCBN_BOOL_EXPRESSION_HH

# include <string>
#include "com/centreon/broker/bam/configuration/bool_expression.hh"

using namespace com::centreon::broker::bam::configuration;

/**
 * Constructor
 *
 *  @param[in] id             BA id.
 *  @param[in] impact         BA impact.
 *  @param[in] expression     BA expression.
 *  @param[in] impact_if      BA impact_if
 *  @param[in] state          BA state.
 *
 */
bool_expression::bool_expression(
				 unsigned int         id,
				 double               impact,
				 std::string const&   expression,
				 bool                 impact_if,
				 bool                 state
				  ):
  _id(id),
  _impact(impact),
  _expression(expression),
  _impact_if(impact_if),
  _state(state)
{}

/**
 *  assignment operator
 *
 *  @param[in] other
 *  @return    this
 */
bool_expression& bool_expression::operator=(bool_expression const& other) {
  if( &other != this){
    _id=other._id;
    _impact=other._impact ;
    _expression=other._expression;
    _impact_if=other._impact_if;
    _state=other._state;
  }
  return *this;
}

/*
 *  Destructor
 */
bool_expression::~bool_expression(){}

/**
 *  get id
 *
 *  @return The id
 *
 */
unsigned int bool_expression::get_id() const {
  return (_id);
}

/**
 *  Get the impact
 *  @return The impact
 *
 */
double bool_expression::get_impact() const {
  return (_impact);
}

/**
 *  get boolean expression
 *
 *  @return The textual representation of the expression
 */
std::string const& bool_expression::get_expression() const {
  return (_expression);
}

/**
 *  get impactIf
 *
 *  @result get whether the the expression impacts
 */
bool bool_expression::get_impact_if() const {
  return (_impact_if);
}

/**
 *  get_state
 *
 *  @result Gets the current state
 */
bool bool_expression::get_state() const {
  return (_state);
}

/**
 *  set impact
 *
 *  @param[in] impact value
 */
void bool_expression::set_impact(double d) {
  _impact = d;
}

/**
 *  set expression
 *
 *  @param[in]  set the textual value for the expression
 */
void bool_expression::set_expression(const std::string& s) {
  _expression = s;
}

/**
 *  set impactIf
 *
 *  @param[in]  sets whether the resulting value is to be considered
 */
void bool_expression::set_impact_if( bool b) {
  _impact_if = b;
}

/**
 *  set state
 *
 *  @param[in]  Set the current state of the expression
 */
void bool_expression::set_state(bool s) {
  _state = s;
}

#endif //! CCBN_BOOL_EXPRESSION_HH

<commit_msg> Style<commit_after>/*
** Copyright 2009-2014 Merethis
**
** This file is part of Centreon Broker.
**
** Centreon Broker is free software: you can redistribute it and/or
** modify it under the terms of the GNU General Public License version 2
** as published by the Free Software Foundation.
**
** Centreon Broker is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
** General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with Centreon Broker. If not, see
** <http://www.gnu.org/licenses/>.
*/
#ifndef CCBN_BOOL_EXPRESSION_HH
# define CCBN_BOOL_EXPRESSION_HH

# include <string>
#include "com/centreon/broker/bam/configuration/bool_expression.hh"

using namespace com::centreon::broker::bam::configuration;

/**
 * Constructor
 *
 *  @param[in] id             BA id.
 *  @param[in] impact         BA impact.
 *  @param[in] expression     BA expression.
 *  @param[in] impact_if      BA impact_if
 *  @param[in] state          BA state.
 *
 */
bool_expression::bool_expression(
                               unsigned int         id,
                               double               impact,
			       std::string const&   expression,
			       bool                 impact_if,
			       bool                 state)
 : _id(id),
  _impact(impact),
  _expression(expression),
  _impact_if(impact_if),
  _state(state)
{}

/**
 *  assignment operator
 *
 *  @param[in] other
 *  @return    this
 */
bool_expression& bool_expression::operator=(bool_expression const& other) {
  if( &other != this){
    _id=other._id;
    _impact=other._impact ;
    _expression=other._expression;
    _impact_if=other._impact_if;
    _state=other._state;
  }
  return *this;
}

/*
 *  Destructor
 */
bool_expression::~bool_expression(){}

/**
 *  get id
 *
 *  @return The id
 *
 */
unsigned int bool_expression::get_id() const {
  return (_id);
}

/**
 *  Get the impact
 *  @return The impact
 *
 */
double bool_expression::get_impact() const {
  return (_impact);
}

/**
 *  get boolean expression
 *
 *  @return The textual representation of the expression
 */
std::string const& bool_expression::get_expression() const {
  return (_expression);
}

/**
 *  get impactIf
 *
 *  @result get whether the the expression impacts
 */
bool bool_expression::get_impact_if() const {
  return (_impact_if);
}

/**
 *  get_state
 *
 *  @result Gets the current state
 */
bool bool_expression::get_state() const {
  return (_state);
}

/**
 *  set impact
 *
 *  @param[in] impact value
 */
void bool_expression::set_impact(double d) {
  _impact = d;
}

/**
 *  set expression
 *
 *  @param[in]  set the textual value for the expression
 */
void bool_expression::set_expression(const std::string& s) {
  _expression = s;
}

/**
 *  set impactIf
 *
 *  @param[in]  sets whether the resulting value is to be considered
 */
void bool_expression::set_impact_if( bool b) {
  _impact_if = b;
}

/**
 *  set state
 *
 *  @param[in]  Set the current state of the expression
 */
void bool_expression::set_state(bool s) {
  _state = s;
}

#endif //! CCBN_BOOL_EXPRESSION_HH

<|endoftext|>


<commit_before>
#include "forms/textbutton.h"
#include "framework/framework.h"

namespace OpenApoc
{

TextButton::TextButton(Framework &fw, Control *Owner, UString Text,
                       std::shared_ptr<BitmapFont> font)
    : Control(fw, Owner), text(Text), font(font),
      buttonbackground(fw.data->load_image("UI/TEXTBUTTONBACK.PNG")),
      TextHAlign(HorizontalAlignment::Centre), TextVAlign(VerticalAlignment::Centre),
      RenderStyle(TextButtonRenderStyles::MenuButtonStyle)
{
	this->buttonclick = fw.data->load_sample("xcom3/RAWSOUND/STRATEGC/INTRFACE/BUTTON1.RAW");
	cached = nullptr;
}

TextButton::~TextButton() {}

void TextButton::EventOccured(Event *e)
{
	Control::EventOccured(e);

	if (e->Type == EVENT_FORM_INTERACTION && e->Data.Forms.RaisedBy == this &&
	    e->Data.Forms.EventFlag == FormEventType::MouseDown)
	{
		fw.soundBackend->playSample(buttonclick);
	}

	if (e->Type == EVENT_FORM_INTERACTION && e->Data.Forms.RaisedBy == this &&
	    e->Data.Forms.EventFlag == FormEventType::MouseClick)
	{
		auto ce = new Event();
		ce->Type = e->Type;
		ce->Data.Forms = e->Data.Forms;
		ce->Data.Forms.EventFlag = FormEventType::ButtonClick;
		fw.PushEvent(ce);
	}
}

void TextButton::OnRender()
{
	if (cached == nullptr || cached->size != Vec2<unsigned int>{Size.x, Size.y})
	{
		cached.reset(new Surface{Vec2<unsigned int>{Size.x, Size.y}});

		RendererSurfaceBinding b(*fw.renderer, cached);

		switch (RenderStyle)
		{
			case TextButtonRenderStyles::SolidButtonStyle:
				fw.renderer->drawFilledRect(Vec2<float>{0, 0}, Vec2<float>{Size.x, Size.y},
				                            BackgroundColour);
				break;
			case TextButtonRenderStyles::MenuButtonStyle:
				fw.renderer->drawScaled(buttonbackground, Vec2<float>{0, 0},
				                        Vec2<float>{Size.x, Size.y});
				fw.renderer->drawFilledRect(Vec2<float>{3, 3}, Vec2<float>{Size.x - 6, Size.y - 6},
				                            Colour{160, 160, 160});
				fw.renderer->drawLine(Vec2<float>{2, 4}, Vec2<float>{Size.x - 2, 4},
				                      Colour{220, 220, 220});
				fw.renderer->drawLine(Vec2<float>{2, Size.y - 4},
				                      Vec2<float>{Size.x - 2, Size.y - 4}, Colour{80, 80, 80});
				fw.renderer->drawLine(Vec2<float>{2, Size.y - 3},
				                      Vec2<float>{Size.x - 2, Size.y - 3}, Colour{64, 64, 64});
				fw.renderer->drawRect(Vec2<float>{3, 3}, Vec2<float>{Size.x - 3, Size.y - 3},
				                      Colour{48, 48, 48});
				break;
		}

		int xpos;
		int ypos;
		std::list<UString> lines = WordWrapText(font, text);

		switch (TextVAlign)
		{
			case VerticalAlignment::Top:
				ypos = 0;
				break;
			case VerticalAlignment::Centre:
				ypos = (Size.y / 2) - ((font->GetFontHeight() * lines.size()) / 2);
				break;
			case VerticalAlignment::Bottom:
				ypos = Size.y - (font->GetFontHeight() * lines.size());
				break;
			default:
				LogError("Unknown TextVAlign");
				return;
		}

		while (lines.size() > 0)
		{
			switch (TextHAlign)
			{
				case HorizontalAlignment::Left:
					xpos = 0;
					break;
				case HorizontalAlignment::Centre:
					xpos = (Size.x / 2) - (font->GetFontWidth(lines.front()) / 2);
					break;
				case HorizontalAlignment::Right:
					xpos = Size.x - font->GetFontWidth(lines.front());
					break;
				default:
					LogError("Unknown TextHAlign");
					return;
			}

			auto textImage = font->getString(lines.front());
			fw.renderer->draw(textImage, Vec2<float>{xpos, ypos});

			lines.pop_front();
			ypos += font->GetFontHeight();
		}
	}
	fw.renderer->draw(cached, Vec2<float>{0, 0});

	if (mouseDepressed && mouseInside)
	{
		switch (RenderStyle)
		{
			case TextButtonRenderStyles::SolidButtonStyle:
				fw.renderer->drawFilledRect(Vec2<float>{0, 0}, Vec2<float>{Size.x, Size.y},
				                            Colour{255, 255, 255});
				break;
			case TextButtonRenderStyles::MenuButtonStyle:
				fw.renderer->drawRect(Vec2<float>{1, 1}, Vec2<float>{Size.x - 2, Size.y - 2},
				                      Colour{255, 255, 255}, 2);
				break;
		}
	}
}

void TextButton::Update()
{
	// No "updates"
}

void TextButton::UnloadResources() {}

UString TextButton::GetText() { return text; }

void TextButton::SetText(UString Text) { text = Text; }

std::shared_ptr<BitmapFont> TextButton::GetFont() { return font; }

void TextButton::SetFont(std::shared_ptr<BitmapFont> NewFont) { font = NewFont; }

}; // namespace OpenApoc
<commit_msg>Fix undefined surface values in TextButton<commit_after>
#include "forms/textbutton.h"
#include "framework/framework.h"

namespace OpenApoc
{

TextButton::TextButton(Framework &fw, Control *Owner, UString Text,
                       std::shared_ptr<BitmapFont> font)
    : Control(fw, Owner), text(Text), font(font),
      buttonbackground(fw.data->load_image("UI/TEXTBUTTONBACK.PNG")),
      TextHAlign(HorizontalAlignment::Centre), TextVAlign(VerticalAlignment::Centre),
      RenderStyle(TextButtonRenderStyles::MenuButtonStyle)
{
	this->buttonclick = fw.data->load_sample("xcom3/RAWSOUND/STRATEGC/INTRFACE/BUTTON1.RAW");
	cached = nullptr;
}

TextButton::~TextButton() {}

void TextButton::EventOccured(Event *e)
{
	Control::EventOccured(e);

	if (e->Type == EVENT_FORM_INTERACTION && e->Data.Forms.RaisedBy == this &&
	    e->Data.Forms.EventFlag == FormEventType::MouseDown)
	{
		fw.soundBackend->playSample(buttonclick);
	}

	if (e->Type == EVENT_FORM_INTERACTION && e->Data.Forms.RaisedBy == this &&
	    e->Data.Forms.EventFlag == FormEventType::MouseClick)
	{
		auto ce = new Event();
		ce->Type = e->Type;
		ce->Data.Forms = e->Data.Forms;
		ce->Data.Forms.EventFlag = FormEventType::ButtonClick;
		fw.PushEvent(ce);
	}
}

void TextButton::OnRender()
{
	if (cached == nullptr || cached->size != Vec2<unsigned int>{Size.x, Size.y})
	{
		cached.reset(new Surface{Vec2<unsigned int>{Size.x, Size.y}});

		RendererSurfaceBinding b(*fw.renderer, cached);
		fw.renderer->clear();

		switch (RenderStyle)
		{
			case TextButtonRenderStyles::SolidButtonStyle:
				fw.renderer->drawFilledRect(Vec2<float>{0, 0}, Vec2<float>{Size.x, Size.y},
				                            BackgroundColour);
				break;
			case TextButtonRenderStyles::MenuButtonStyle:
				fw.renderer->drawScaled(buttonbackground, Vec2<float>{0, 0},
				                        Vec2<float>{Size.x, Size.y});
				fw.renderer->drawFilledRect(Vec2<float>{3, 3}, Vec2<float>{Size.x - 6, Size.y - 6},
				                            Colour{160, 160, 160});
				fw.renderer->drawLine(Vec2<float>{2, 4}, Vec2<float>{Size.x - 2, 4},
				                      Colour{220, 220, 220});
				fw.renderer->drawLine(Vec2<float>{2, Size.y - 4},
				                      Vec2<float>{Size.x - 2, Size.y - 4}, Colour{80, 80, 80});
				fw.renderer->drawLine(Vec2<float>{2, Size.y - 3},
				                      Vec2<float>{Size.x - 2, Size.y - 3}, Colour{64, 64, 64});
				fw.renderer->drawRect(Vec2<float>{3, 3}, Vec2<float>{Size.x - 3, Size.y - 3},
				                      Colour{48, 48, 48});
				break;
		}

		int xpos;
		int ypos;
		std::list<UString> lines = WordWrapText(font, text);

		switch (TextVAlign)
		{
			case VerticalAlignment::Top:
				ypos = 0;
				break;
			case VerticalAlignment::Centre:
				ypos = (Size.y / 2) - ((font->GetFontHeight() * lines.size()) / 2);
				break;
			case VerticalAlignment::Bottom:
				ypos = Size.y - (font->GetFontHeight() * lines.size());
				break;
			default:
				LogError("Unknown TextVAlign");
				return;
		}

		while (lines.size() > 0)
		{
			switch (TextHAlign)
			{
				case HorizontalAlignment::Left:
					xpos = 0;
					break;
				case HorizontalAlignment::Centre:
					xpos = (Size.x / 2) - (font->GetFontWidth(lines.front()) / 2);
					break;
				case HorizontalAlignment::Right:
					xpos = Size.x - font->GetFontWidth(lines.front());
					break;
				default:
					LogError("Unknown TextHAlign");
					return;
			}

			auto textImage = font->getString(lines.front());
			fw.renderer->draw(textImage, Vec2<float>{xpos, ypos});

			lines.pop_front();
			ypos += font->GetFontHeight();
		}
	}
	fw.renderer->draw(cached, Vec2<float>{0, 0});

	if (mouseDepressed && mouseInside)
	{
		switch (RenderStyle)
		{
			case TextButtonRenderStyles::SolidButtonStyle:
				fw.renderer->drawFilledRect(Vec2<float>{0, 0}, Vec2<float>{Size.x, Size.y},
				                            Colour{255, 255, 255});
				break;
			case TextButtonRenderStyles::MenuButtonStyle:
				fw.renderer->drawRect(Vec2<float>{1, 1}, Vec2<float>{Size.x - 2, Size.y - 2},
				                      Colour{255, 255, 255}, 2);
				break;
		}
	}
}

void TextButton::Update()
{
	// No "updates"
}

void TextButton::UnloadResources() {}

UString TextButton::GetText() { return text; }

void TextButton::SetText(UString Text) { text = Text; }

std::shared_ptr<BitmapFont> TextButton::GetFont() { return font; }

void TextButton::SetFont(std::shared_ptr<BitmapFont> NewFont) { font = NewFont; }

}; // namespace OpenApoc
<|endoftext|>


<commit_before>/*
 * Copyright 2007-2019 Content Management AG
 * All rights reserved.
 *
 * author: Max Kellermann <mk@cm4all.com>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * - Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the
 * distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
 * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "Client.hxx"
#include "Error.hxx"
#include "Parser.hxx"
#include "pool/pool.hxx"
#include "pool/LeakDetector.hxx"
#include "istream/Handler.hxx"
#include "istream/UnusedPtr.hxx"
#include "istream/Pointer.hxx"
#include "istream/istream_null.hxx"
#include "http/HeaderParser.hxx"
#include "stopwatch.hxx"
#include "strmap.hxx"
#include "HttpResponseHandler.hxx"
#include "fb_pool.hxx"
#include "SliceFifoBuffer.hxx"
#include "util/Cast.hxx"
#include "util/Cancellable.hxx"
#include "util/DestructObserver.hxx"
#include "util/Exception.hxx"

#include <string.h>
#include <stdlib.h>

class CGIClient final : PoolLeakDetector, Istream, IstreamHandler, Cancellable, DestructAnchor {
    const StopwatchPtr stopwatch;

    IstreamPointer input;
    SliceFifoBuffer buffer;

    CGIParser parser;

    /**
     * This flag is true while cgi_parse_headers() is calling
     * HttpResponseHandler::InvokeResponse().  In this case,
     * istream_read(cgi->input) is already up in the stack, and must
     * not be called again.
     */
    bool in_response_callback;

    bool had_input, had_output;

    HttpResponseHandler &handler;

public:
    CGIClient(struct pool &_pool, StopwatchPtr &&_stopwatch,
              UnusedIstreamPtr _input,
              HttpResponseHandler &_handler,
              CancellablePointer &cancel_ptr);

    /**
     * @return false if the connection has been closed
     */
    bool ReturnResponse();

    /**
     * Feed data into the input buffer and continue parsing response
     * headers from it.  After this function returns, the response may
     * have been delivered to the response handler, and the caller should
     * post the rest of the specified buffer to the response body stream.
     *
     * Caller must hold pool reference.
     *
     * @return the number of bytes consumed from the specified buffer
     * (moved to the input buffer), 0 if the object has been closed
     */
    size_t FeedHeaders(const void *data, size_t length);

    /**
     * Call FeedHeaders() in a loop, to parse as much as possible.
     *
     * Caller must hold pool reference.
     */
    size_t FeedHeadersLoop(const char *data, size_t length);

    /**
     * Caller must hold pool reference.
     */
    size_t FeedHeadersCheck(const char *data, size_t length);

    size_t FeedBody(const char *data, size_t length);

    /* virtual methods from class Cancellable */
    void Cancel() noexcept override;

    /* virtual methods from class Istream */
    off_t _GetAvailable(bool partial) noexcept override;
    void _Read() noexcept override;
    void _Close() noexcept override;

    /* virtual methods from class IstreamHandler */
    size_t OnData(const void *data, size_t length) noexcept override;
    ssize_t OnDirect(FdType type, int fd, size_t max_length) noexcept override;
    void OnEof() noexcept override;
    void OnError(std::exception_ptr ep) noexcept override;
};

inline bool
CGIClient::ReturnResponse()
{
    http_status_t status = parser.GetStatus();
    StringMap &headers = parser.GetHeaders();

    if (http_status_is_empty(status)) {
        /* this response does not have a response body, as indicated
           by the HTTP status code */

        stopwatch.RecordEvent("empty");

        buffer.Free();
        input.ClearAndClose();

        auto &_handler = handler;
        Destroy();
        _handler.InvokeResponse(status, std::move(headers), UnusedIstreamPtr());
        return false;
    } else if (parser.IsEOF()) {
        /* the response body is empty */

        stopwatch.RecordEvent("empty");

        buffer.Free();
        input.ClearAndClose();
        auto &_handler = handler;
        Destroy();
        _handler.InvokeResponse(status, std::move(headers),
                                istream_null_new(GetPool()));
        return false;
    } else {
        stopwatch.RecordEvent("headers");

        const DestructObserver destructed(*this);

        in_response_callback = true;
        handler.InvokeResponse(status, std::move(headers),
                               UnusedIstreamPtr(this));
        if (destructed)
            return false;

        in_response_callback = false;
        return true;
    }
}

inline size_t
CGIClient::FeedHeaders(const void *data, size_t length)
try {
    assert(!parser.AreHeadersFinished());

    auto w = buffer.Write();
    assert(!w.empty());

    if (length > w.size)
        length = w.size;

    memcpy(w.data, data, length);
    buffer.Append(length);

    switch (parser.FeedHeaders(GetPool(), buffer)) {
    case Completion::DONE:
        /* the DONE status can only be triggered by new data that
           was just received; therefore, the amount of data still in
           the buffer (= response body) must be smaller */
        assert(buffer.GetAvailable() < length);

        if (!ReturnResponse())
            return 0;

        /* don't consider data still in the buffer (= response body)
           as "consumed"; the caller will attempt to submit it to the
           response body handler */
        return length - buffer.GetAvailable();

    case Completion::MORE:
        return length;

    case Completion::CLOSED:
        /* unreachable */
        assert(false);
        return 0;
    }

    /* unreachable */
    assert(false);
    return 0;
} catch (...) {
    buffer.Free();
    input.ClearAndClose();
    auto &_handler = handler;
    Destroy();
    _handler.InvokeError(std::current_exception());
    return 0;
}

inline size_t
CGIClient::FeedHeadersLoop(const char *data, size_t length)
{
    assert(length > 0);
    assert(!parser.AreHeadersFinished());

    const DestructObserver destructed(*this);
    size_t consumed = 0;

    do {
        size_t nbytes = FeedHeaders(data + consumed, length - consumed);
        if (nbytes == 0)
            break;

        consumed += nbytes;
    } while (consumed < length && !parser.AreHeadersFinished());

    if (destructed)
        return 0;

    return consumed;
}

inline size_t
CGIClient::FeedHeadersCheck(const char *data, size_t length)
{
    size_t nbytes = FeedHeadersLoop(data, length);

    assert(nbytes == 0 || input.IsDefined());
    assert(nbytes == 0 ||
           !parser.AreHeadersFinished() ||
           !parser.IsEOF());

    return nbytes;
}

inline size_t
CGIClient::FeedBody(const char *data, size_t length)
{
    if (parser.IsTooMuch(length)) {
        stopwatch.RecordEvent("malformed");

        buffer.Free();
        input.ClearAndClose();

        DestroyError(std::make_exception_ptr(CgiError("too much data from CGI script")));
        return 0;
    }

    had_output = true;

    size_t nbytes = InvokeData(data, length);
    if (nbytes > 0 && parser.BodyConsumed(nbytes)) {
        stopwatch.RecordEvent("end");

        buffer.Free();
        input.ClearAndClose();
        DestroyEof();
        return 0;
    }

    return nbytes;
}

/*
 * input handler
 *
 */

size_t
CGIClient::OnData(const void *data, size_t length) noexcept
{
    assert(input.IsDefined());

    had_input = true;

    if (!parser.AreHeadersFinished()) {
        size_t nbytes = FeedHeadersCheck((const char *)data, length);

        if (nbytes > 0 && nbytes < length &&
            parser.AreHeadersFinished()) {
            /* the headers are finished; now begin sending the
               response body */
            const DestructObserver destructed(*this);
            size_t nbytes2 = FeedBody((const char *)data + nbytes,
                                      length - nbytes);
            if (nbytes2 > 0)
                /* more data was consumed */
                nbytes += nbytes2;
            else if (destructed)
                /* the connection was closed, must return 0 */
                nbytes = 0;
        }

        return nbytes;
    } else {
        return FeedBody((const char *)data, length);
    }
}

ssize_t
CGIClient::OnDirect(FdType type, int fd, size_t max_length) noexcept
{
    assert(parser.AreHeadersFinished());

    had_input = true;
    had_output = true;

    if (parser.KnownLength() &&
        (off_t)max_length > parser.GetAvailable())
        max_length = (size_t)parser.GetAvailable();

    ssize_t nbytes = InvokeDirect(type, fd, max_length);
    if (nbytes > 0 && parser.BodyConsumed(nbytes)) {
        stopwatch.RecordEvent("end");

        buffer.Free();
        input.Close();
        DestroyEof();
        return ISTREAM_RESULT_CLOSED;
    }

    return nbytes;
}

void
CGIClient::OnEof() noexcept
{
    input.Clear();

    if (!parser.AreHeadersFinished()) {
        stopwatch.RecordEvent("malformed");

        assert(!HasHandler());

        buffer.Free();

        auto &_handler = handler;
        Destroy();
        _handler.InvokeError(std::make_exception_ptr(CgiError("premature end of headers from CGI script")));
    } else if (parser.DoesRequireMore()) {
        stopwatch.RecordEvent("malformed");

        buffer.Free();

        DestroyError(std::make_exception_ptr(CgiError("premature end of response body from CGI script")));
    } else {
        stopwatch.RecordEvent("end");

        buffer.Free();
        DestroyEof();
    }
}

void
CGIClient::OnError(std::exception_ptr ep) noexcept
{
    stopwatch.RecordEvent("error");

    input.Clear();

    if (!parser.AreHeadersFinished()) {
        /* the response hasn't been sent yet: notify the response
           handler */
        assert(!HasHandler());

        buffer.Free();

        auto &_handler = handler;
        Destroy();
        _handler.InvokeError(NestException(ep,
                                           std::runtime_error("CGI request body failed")));
    } else {
        /* response has been sent: abort only the output stream */
        buffer.Free();
        DestroyError(ep);
    }
}

/*
 * istream implementation
 *
 */

off_t
CGIClient::_GetAvailable(bool partial) noexcept
{
    if (parser.KnownLength())
        return parser.GetAvailable();

    if (!input.IsDefined())
        return 0;

    if (in_response_callback)
        /* this condition catches the case in cgi_parse_headers():
           HttpResponseHandler::InvokeResponse() might
           recursively call istream_read(input) */
        return (off_t)-1;

    return input.GetAvailable(partial);
}

void
CGIClient::_Read() noexcept
{
    if (input.IsDefined()) {
        input.SetDirect(GetHandlerDirect());

        /* this condition catches the case in cgi_parse_headers():
           HttpResponseHandler::InvokeResponse() might
           recursively call input.Read() */
        if (in_response_callback) {
            return;
        }

        const DestructObserver destructed(*this);

        had_output = false;
        do {
            had_input = false;
            input.Read();
        } while (!destructed && input.IsDefined() && had_input && !had_output);
    }
}

void
CGIClient::_Close() noexcept
{
    buffer.Free();

    if (input.IsDefined())
        input.ClearAndClose();

    Destroy();
}

/*
 * async operation
 *
 */

void
CGIClient::Cancel() noexcept
{
    assert(input.IsDefined());

    buffer.Free();
    input.Close();
    Destroy();
}

/*
 * constructor
 *
 */

inline
CGIClient::CGIClient(struct pool &_pool, StopwatchPtr &&_stopwatch,
                     UnusedIstreamPtr _input,
                     HttpResponseHandler &_handler,
                     CancellablePointer &cancel_ptr)
    :PoolLeakDetector(_pool), Istream(_pool),
     stopwatch(std::move(_stopwatch)),
     input(std::move(_input), *this),
     buffer(fb_pool_get()),
     parser(_pool),
     handler(_handler)
{
    cancel_ptr = *this;

    input.Read();
}

void
cgi_client_new(struct pool &pool, StopwatchPtr stopwatch,
               UnusedIstreamPtr input,
               HttpResponseHandler &handler,
               CancellablePointer &cancel_ptr)
{
    NewFromPool<CGIClient>(pool, pool, std::move(stopwatch),
                           std::move(input),
                           handler, cancel_ptr);
}
<commit_msg>Revert "cgi/Client: use class PoolLeakDetector"<commit_after>/*
 * Copyright 2007-2019 Content Management AG
 * All rights reserved.
 *
 * author: Max Kellermann <mk@cm4all.com>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * - Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the
 * distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
 * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "Client.hxx"
#include "Error.hxx"
#include "Parser.hxx"
#include "pool/pool.hxx"
#include "istream/Handler.hxx"
#include "istream/UnusedPtr.hxx"
#include "istream/Pointer.hxx"
#include "istream/istream_null.hxx"
#include "http/HeaderParser.hxx"
#include "stopwatch.hxx"
#include "strmap.hxx"
#include "HttpResponseHandler.hxx"
#include "fb_pool.hxx"
#include "SliceFifoBuffer.hxx"
#include "util/Cast.hxx"
#include "util/Cancellable.hxx"
#include "util/DestructObserver.hxx"
#include "util/Exception.hxx"

#include <string.h>
#include <stdlib.h>

class CGIClient final : Istream, IstreamHandler, Cancellable, DestructAnchor {
    const StopwatchPtr stopwatch;

    IstreamPointer input;
    SliceFifoBuffer buffer;

    CGIParser parser;

    /**
     * This flag is true while cgi_parse_headers() is calling
     * HttpResponseHandler::InvokeResponse().  In this case,
     * istream_read(cgi->input) is already up in the stack, and must
     * not be called again.
     */
    bool in_response_callback;

    bool had_input, had_output;

    HttpResponseHandler &handler;

public:
    CGIClient(struct pool &_pool, StopwatchPtr &&_stopwatch,
              UnusedIstreamPtr _input,
              HttpResponseHandler &_handler,
              CancellablePointer &cancel_ptr);

    /**
     * @return false if the connection has been closed
     */
    bool ReturnResponse();

    /**
     * Feed data into the input buffer and continue parsing response
     * headers from it.  After this function returns, the response may
     * have been delivered to the response handler, and the caller should
     * post the rest of the specified buffer to the response body stream.
     *
     * Caller must hold pool reference.
     *
     * @return the number of bytes consumed from the specified buffer
     * (moved to the input buffer), 0 if the object has been closed
     */
    size_t FeedHeaders(const void *data, size_t length);

    /**
     * Call FeedHeaders() in a loop, to parse as much as possible.
     *
     * Caller must hold pool reference.
     */
    size_t FeedHeadersLoop(const char *data, size_t length);

    /**
     * Caller must hold pool reference.
     */
    size_t FeedHeadersCheck(const char *data, size_t length);

    size_t FeedBody(const char *data, size_t length);

    /* virtual methods from class Cancellable */
    void Cancel() noexcept override;

    /* virtual methods from class Istream */
    off_t _GetAvailable(bool partial) noexcept override;
    void _Read() noexcept override;
    void _Close() noexcept override;

    /* virtual methods from class IstreamHandler */
    size_t OnData(const void *data, size_t length) noexcept override;
    ssize_t OnDirect(FdType type, int fd, size_t max_length) noexcept override;
    void OnEof() noexcept override;
    void OnError(std::exception_ptr ep) noexcept override;
};

inline bool
CGIClient::ReturnResponse()
{
    http_status_t status = parser.GetStatus();
    StringMap &headers = parser.GetHeaders();

    if (http_status_is_empty(status)) {
        /* this response does not have a response body, as indicated
           by the HTTP status code */

        stopwatch.RecordEvent("empty");

        buffer.Free();
        input.ClearAndClose();

        auto &_handler = handler;
        Destroy();
        _handler.InvokeResponse(status, std::move(headers), UnusedIstreamPtr());
        return false;
    } else if (parser.IsEOF()) {
        /* the response body is empty */

        stopwatch.RecordEvent("empty");

        buffer.Free();
        input.ClearAndClose();
        auto &_handler = handler;
        Destroy();
        _handler.InvokeResponse(status, std::move(headers),
                                istream_null_new(GetPool()));
        return false;
    } else {
        stopwatch.RecordEvent("headers");

        const DestructObserver destructed(*this);

        in_response_callback = true;
        handler.InvokeResponse(status, std::move(headers),
                               UnusedIstreamPtr(this));
        if (destructed)
            return false;

        in_response_callback = false;
        return true;
    }
}

inline size_t
CGIClient::FeedHeaders(const void *data, size_t length)
try {
    assert(!parser.AreHeadersFinished());

    auto w = buffer.Write();
    assert(!w.empty());

    if (length > w.size)
        length = w.size;

    memcpy(w.data, data, length);
    buffer.Append(length);

    switch (parser.FeedHeaders(GetPool(), buffer)) {
    case Completion::DONE:
        /* the DONE status can only be triggered by new data that
           was just received; therefore, the amount of data still in
           the buffer (= response body) must be smaller */
        assert(buffer.GetAvailable() < length);

        if (!ReturnResponse())
            return 0;

        /* don't consider data still in the buffer (= response body)
           as "consumed"; the caller will attempt to submit it to the
           response body handler */
        return length - buffer.GetAvailable();

    case Completion::MORE:
        return length;

    case Completion::CLOSED:
        /* unreachable */
        assert(false);
        return 0;
    }

    /* unreachable */
    assert(false);
    return 0;
} catch (...) {
    buffer.Free();
    input.ClearAndClose();
    auto &_handler = handler;
    Destroy();
    _handler.InvokeError(std::current_exception());
    return 0;
}

inline size_t
CGIClient::FeedHeadersLoop(const char *data, size_t length)
{
    assert(length > 0);
    assert(!parser.AreHeadersFinished());

    const DestructObserver destructed(*this);
    size_t consumed = 0;

    do {
        size_t nbytes = FeedHeaders(data + consumed, length - consumed);
        if (nbytes == 0)
            break;

        consumed += nbytes;
    } while (consumed < length && !parser.AreHeadersFinished());

    if (destructed)
        return 0;

    return consumed;
}

inline size_t
CGIClient::FeedHeadersCheck(const char *data, size_t length)
{
    size_t nbytes = FeedHeadersLoop(data, length);

    assert(nbytes == 0 || input.IsDefined());
    assert(nbytes == 0 ||
           !parser.AreHeadersFinished() ||
           !parser.IsEOF());

    return nbytes;
}

inline size_t
CGIClient::FeedBody(const char *data, size_t length)
{
    if (parser.IsTooMuch(length)) {
        stopwatch.RecordEvent("malformed");

        buffer.Free();
        input.ClearAndClose();

        DestroyError(std::make_exception_ptr(CgiError("too much data from CGI script")));
        return 0;
    }

    had_output = true;

    size_t nbytes = InvokeData(data, length);
    if (nbytes > 0 && parser.BodyConsumed(nbytes)) {
        stopwatch.RecordEvent("end");

        buffer.Free();
        input.ClearAndClose();
        DestroyEof();
        return 0;
    }

    return nbytes;
}

/*
 * input handler
 *
 */

size_t
CGIClient::OnData(const void *data, size_t length) noexcept
{
    assert(input.IsDefined());

    had_input = true;

    if (!parser.AreHeadersFinished()) {
        size_t nbytes = FeedHeadersCheck((const char *)data, length);

        if (nbytes > 0 && nbytes < length &&
            parser.AreHeadersFinished()) {
            /* the headers are finished; now begin sending the
               response body */
            const DestructObserver destructed(*this);
            size_t nbytes2 = FeedBody((const char *)data + nbytes,
                                      length - nbytes);
            if (nbytes2 > 0)
                /* more data was consumed */
                nbytes += nbytes2;
            else if (destructed)
                /* the connection was closed, must return 0 */
                nbytes = 0;
        }

        return nbytes;
    } else {
        return FeedBody((const char *)data, length);
    }
}

ssize_t
CGIClient::OnDirect(FdType type, int fd, size_t max_length) noexcept
{
    assert(parser.AreHeadersFinished());

    had_input = true;
    had_output = true;

    if (parser.KnownLength() &&
        (off_t)max_length > parser.GetAvailable())
        max_length = (size_t)parser.GetAvailable();

    ssize_t nbytes = InvokeDirect(type, fd, max_length);
    if (nbytes > 0 && parser.BodyConsumed(nbytes)) {
        stopwatch.RecordEvent("end");

        buffer.Free();
        input.Close();
        DestroyEof();
        return ISTREAM_RESULT_CLOSED;
    }

    return nbytes;
}

void
CGIClient::OnEof() noexcept
{
    input.Clear();

    if (!parser.AreHeadersFinished()) {
        stopwatch.RecordEvent("malformed");

        assert(!HasHandler());

        buffer.Free();

        auto &_handler = handler;
        Destroy();
        _handler.InvokeError(std::make_exception_ptr(CgiError("premature end of headers from CGI script")));
    } else if (parser.DoesRequireMore()) {
        stopwatch.RecordEvent("malformed");

        buffer.Free();

        DestroyError(std::make_exception_ptr(CgiError("premature end of response body from CGI script")));
    } else {
        stopwatch.RecordEvent("end");

        buffer.Free();
        DestroyEof();
    }
}

void
CGIClient::OnError(std::exception_ptr ep) noexcept
{
    stopwatch.RecordEvent("error");

    input.Clear();

    if (!parser.AreHeadersFinished()) {
        /* the response hasn't been sent yet: notify the response
           handler */
        assert(!HasHandler());

        buffer.Free();

        auto &_handler = handler;
        Destroy();
        _handler.InvokeError(NestException(ep,
                                           std::runtime_error("CGI request body failed")));
    } else {
        /* response has been sent: abort only the output stream */
        buffer.Free();
        DestroyError(ep);
    }
}

/*
 * istream implementation
 *
 */

off_t
CGIClient::_GetAvailable(bool partial) noexcept
{
    if (parser.KnownLength())
        return parser.GetAvailable();

    if (!input.IsDefined())
        return 0;

    if (in_response_callback)
        /* this condition catches the case in cgi_parse_headers():
           HttpResponseHandler::InvokeResponse() might
           recursively call istream_read(input) */
        return (off_t)-1;

    return input.GetAvailable(partial);
}

void
CGIClient::_Read() noexcept
{
    if (input.IsDefined()) {
        input.SetDirect(GetHandlerDirect());

        /* this condition catches the case in cgi_parse_headers():
           HttpResponseHandler::InvokeResponse() might
           recursively call input.Read() */
        if (in_response_callback) {
            return;
        }

        const DestructObserver destructed(*this);

        had_output = false;
        do {
            had_input = false;
            input.Read();
        } while (!destructed && input.IsDefined() && had_input && !had_output);
    }
}

void
CGIClient::_Close() noexcept
{
    buffer.Free();

    if (input.IsDefined())
        input.ClearAndClose();

    Destroy();
}

/*
 * async operation
 *
 */

void
CGIClient::Cancel() noexcept
{
    assert(input.IsDefined());

    buffer.Free();
    input.Close();
    Destroy();
}

/*
 * constructor
 *
 */

inline
CGIClient::CGIClient(struct pool &_pool, StopwatchPtr &&_stopwatch,
                     UnusedIstreamPtr _input,
                     HttpResponseHandler &_handler,
                     CancellablePointer &cancel_ptr)
    :Istream(_pool),
     stopwatch(std::move(_stopwatch)),
     input(std::move(_input), *this),
     buffer(fb_pool_get()),
     parser(_pool),
     handler(_handler)
{
    cancel_ptr = *this;

    input.Read();
}

void
cgi_client_new(struct pool &pool, StopwatchPtr stopwatch,
               UnusedIstreamPtr input,
               HttpResponseHandler &handler,
               CancellablePointer &cancel_ptr)
{
    NewFromPool<CGIClient>(pool, pool, std::move(stopwatch),
                           std::move(input),
                           handler, cancel_ptr);
}
<|endoftext|>