<commit_before>// Copyright (c) 2016 Alonzo Coeus
#include "crypto/lamport.h"

#include "crypto/ripemd160.h"
#include "crypto/common.h"
#include "uint256.h"

using namespace std;
typedef vector<unsigned char> valtype;

bool LAMPORT::checksig(valtype *pointerdata, valtype *pointerasig, valtype *pointerrootkey, valtype *pointermerklewit)
{

valtype data = *pointerdata;
valtype asig = *pointerasig;
valtype rootkey = *pointerrootkey;
valtype merklewit = *pointermerklewit;

//STARTING to convert asig vector to sig[20][2][20]
unsigned char sig[160/(LAMPORT::chunksize*8)][2][20];
for(int i = 0; i < (LAMPORT::chunksize*8); i++)
{
  for(int o = 0; o < 2; o++)
  {
    for(int p = 0; p < 20; p++)
    {
      sig[i][o][p] = asig[p + (o * 20) + (p * 40)];
    }
  }
}
//END converting asig vector to sig[20][2][20]

  valtype exmerklewit[8]; /*this is the merkle wit minus the main public key max number of publickeys to rootkey is 256 due to 2^n where n is the first array index is 8*/
  char pubkey[160/(LAMPORT::chunksize*8)][2][20];
  valtype hashablepubkey;

  //START converting merkle wit to exmerklewit and public key
  char merklebuffer[800]; //size of publickey is the max size of the buffer
  unsigned int i;
  for(i = 0; i < sizeof(merklewit); i++)
  {
    if(merklewit[i] == 0x00 && merklewit[i+1] == 0x00) /*test for partition beetween merkle segments*/
      break;
    merklebuffer[i] = merklewit[i];
  }

  memcpy(&(pubkey[0][0][0]), &(merklebuffer[0]), sizeof(merklebuffer));
  memcpy(&(hashablepubkey[0]), &(merklebuffer[0]), sizeof(merklebuffer));

  int o = 0;
  int r = 0; //number of times we have reset o count
  for(; i < merklewit.size(); i++)
  {
    if(merklewit[i] == 0x00 && merklewit[i+1] == 0x00)
    {
      if(r == 8)
        break; //lim of exmerklewit
      memcpy(&(exmerklewit[r][0]), &(merklebuffer[0]), sizeof(merklebuffer));
      r++;
      i++; //get i+1 index chunk so we can jump to next part of the merklewit at the end of cycle
      o = 0; //merklebuffer index
    }
    else
    {
      merklebuffer[o] = merklewit[i];
      o++;
    }
  }
  //END decoding merkle wit format

  //START checking if new publickey is a part of the root key
  valtype tempverifyhash;
  CRIPEMD160().Write(&(hashablepubkey[0]), hashablepubkey.size()).Finalize(&(tempverifyhash[0])); //first element is start of arrays address length pre-def
  for(int i = 0; true; i++) //to end if false we will use return to lower processing time
  {
    if(exmerklewit[i][0] == 0 && exmerklewit[i][1] == 0 && exmerklewit[i][2] == 0 && exmerklewit[i][3] == 0)
    {
      if(tempverifyhash == rootkey)
      {
        break;
      }
      else
      {
        return false;
      }
    }

    CRIPEMD160().Write(&(tempverifyhash[0]), tempverifyhash.size()).Write(&(exmerklewit[i][0]), exmerklewit[i].size()).Finalize(&(tempverifyhash[0]));
  }

  //END checking if new publickey is a part of the root key

  //START checking if sig is valid

  //create hash of data
  valtype hashdata;
  valtype sellectedhashseg; //to allow seamless scaling to larger segment sizes
  uint512_t sellectedinthashseg; //memcpy of valtype to uint512_t format
  CRIPEMD160().Write(&(data[0]), data.size()).Finalize(&(hashdata[0]));
  unsigned char keypair[2][20]; //the two public values at the ends of the ladder
  unsigned char sigpair[2][20]; //the two values from each side of hash ladder when signing

  for(int i = 0; i < 160/(LAMPORT::chunksize*8); i++) {
    //get sig, key pair and sellect hash segments
    uint512_t sellectedinthashseg;
    memcpy(&(sellectedhashseg), &(hashdata[i*LAMPORT::chunksize]), LAMPORT::chunksize);
    memcpy(&(sellectedinthashseg), &(sellectedhashseg), LAMPORT::chunksize);
    for(int o = 0; o < 2; o++) {
      memcpy(&(keypair[o][0]), &(pubkey[i][o]), 20);
      memcpy(&(sigpair[i][0]), &(sig[i][o]), 20);
    }
    //
    uint512_t i_a = 0;
    while (true) { //i-a sigpair[0]
      CRIPEMD160().Write(&(sigpair[0][0]), data.size()).Finalize(&(sigpair[0][0]));
      i_a++; //increment after data hased
      if(i_a == 160) {
        return false;
      }
      if(sigpair[0] == keypair[0]) {
        break;
      }
    }
    uint512_t i_b = 0;
    while (true) { //i-b sigpair[1]
      CRIPEMD160().Write(&(sigpair[1][0]), data.size()).Finalize(&(sigpair[1][0]));
      i_b++;
      if(i_b == 160) {
        return false;
      }
      if(sigpair[1] == keypair[1]) {
        break;
      }
    }
    if((160-i_a != i_b-1) || (160-i_a != sellectedinthashseg)) {
      return false;
    }
  }
  //END checking if sig is valid
  return true; // if compleats all tests return true
}
    unsigned char *LAMPORT::createsig(valtype *pdata, uint512_t *pprikey, int sellectedpubkey)
    {
      /* the signing will happen under this */
      valtype data = *pdata;
      uint512_t prikey = *pprikey;
      valtype pkey[20][2][20];
      unsigned char * hashabledata = new unsigned char[data.size()];

      valtype hash;
      CRIPEMD160().Write(&(hashabledata), data.size()).Finalize(&(hash));

      return &sig[0][0][0];
    }
<commit_msg>Update lamport.cpp<commit_after>// Copyright (c) 2016 Alonzo Coeus
#include "crypto/lamport.h"

#include "crypto/ripemd160.h"
#include "crypto/common.h"
#include "uint256.h"

using namespace std;
typedef vector<unsigned char> valtype;

bool LAMPORT::checksig(valtype *pointerdata, valtype *pointerasig, valtype *pointerrootkey, valtype *pointermerklewit)
{

valtype data = *pointerdata;
valtype asig = *pointerasig;
valtype rootkey = *pointerrootkey;
valtype merklewit = *pointermerklewit;

//STARTING to convert asig vector to sig[20][2][20]
unsigned char sig[160/(LAMPORT::chunksize*8)][2][20];
for(int i = 0; i < (LAMPORT::chunksize*8); i++)
{
  for(int o = 0; o < 2; o++)
  {
    for(int p = 0; p < 20; p++)
    {
      sig[i][o][p] = asig[p + (o * 20) + (p * 40)];
    }
  }
}
//END converting asig vector to sig[20][2][20]

  valtype exmerklewit[8]; /*this is the merkle wit minus the main public key max number of publickeys to rootkey is 256 due to 2^n where n is the first array index is 8*/
  char pubkey[160/(LAMPORT::chunksize*8)][2][20];
  valtype hashablepubkey;

  //START converting merkle wit to exmerklewit and public key
  char merklebuffer[800]; //size of publickey is the max size of the buffer
  unsigned int i;
  for(i = 0; i < sizeof(merklewit); i++)
  {
    if(merklewit[i] == 0x00 && merklewit[i+1] == 0x00) /*test for partition beetween merkle segments*/
      break;
    merklebuffer[i] = merklewit[i];
  }

  memcpy(&(pubkey[0][0][0]), &(merklebuffer[0]), sizeof(merklebuffer));
  memcpy(&(hashablepubkey[0]), &(merklebuffer[0]), sizeof(merklebuffer));

  int o = 0;
  int r = 0; //number of times we have reset o count
  for(; i < merklewit.size(); i++)
  {
    if(merklewit[i] == 0x00 && merklewit[i+1] == 0x00)
    {
      if(r == 8)
        break; //lim of exmerklewit
      memcpy(&(exmerklewit[r][0]), &(merklebuffer[0]), sizeof(merklebuffer));
      r++;
      i++; //get i+1 index chunk so we can jump to next part of the merklewit at the end of cycle
      o = 0; //merklebuffer index
    }
    else
    {
      merklebuffer[o] = merklewit[i];
      o++;
    }
  }
  //END decoding merkle wit format

  //START checking if new publickey is a part of the root key
  valtype tempverifyhash;
  CRIPEMD160().Write(&(hashablepubkey[0]), hashablepubkey.size()).Finalize(&(tempverifyhash[0])); //first element is start of arrays address length pre-def
  for(int i = 0; true; i++) //to end if false we will use return to lower processing time
  {
    if(exmerklewit[i][0] == 0 && exmerklewit[i][1] == 0 && exmerklewit[i][2] == 0 && exmerklewit[i][3] == 0)
    {
      if(tempverifyhash == rootkey)
      {
        break;
      }
      else
      {
        return false;
      }
    }

    CRIPEMD160().Write(&(tempverifyhash[0]), tempverifyhash.size()).Write(&(exmerklewit[i][0]), exmerklewit[i].size()).Finalize(&(tempverifyhash[0]));
  }

  //END checking if new publickey is a part of the root key

  //START checking if sig is valid

  //create hash of data
  valtype hashdata;
  valtype sellectedhashseg; //to allow seamless scaling to larger segment sizes
  uint512_t sellectedinthashseg; //memcpy of valtype to uint512_t format
  CRIPEMD160().Write(&(data[0]), data.size()).Finalize(&(hashdata[0]));
  unsigned char keypair[2][20]; //the two public values at the ends of the ladder
  unsigned char sigpair[2][20]; //the two values from each side of hash ladder when signing

  for(int i = 0; i < 160/(LAMPORT::chunksize*8); i++) {
    //get sig, key pair and sellect hash segments
    uint512_t sellectedinthashseg;
    memcpy(&(sellectedhashseg), &(hashdata[i*LAMPORT::chunksize]), LAMPORT::chunksize);
    memcpy(&(sellectedinthashseg), &(sellectedhashseg), LAMPORT::chunksize);
    for(int o = 0; o < 2; o++) {
      memcpy(&(keypair[o][0]), &(pubkey[i][o]), 20);
      memcpy(&(sigpair[i][0]), &(sig[i][o]), 20);
    }
    //
    uint512_t i_a = 0;
    while (true) { //i-a sigpair[0]
      CRIPEMD160().Write(&(sigpair[0][0]), data.size()).Finalize(&(sigpair[0][0]));
      i_a++; //increment after data hased
      if(i_a == 160) {
        return false;
      }
      if(sigpair[0] == keypair[0]) {
        break;
      }
    }
    uint512_t i_b = 0;
    while (true) { //i-b sigpair[1]
      CRIPEMD160().Write(&(sigpair[1][0]), data.size()).Finalize(&(sigpair[1][0]));
      i_b++;
      if(i_b == 160) {
        return false;
      }
      if(sigpair[1] == keypair[1]) {
        break;
      }
    }
    if((160-i_a != i_b-1) || (160-i_a != sellectedinthashseg)) {
      return false;
    }
  }
  //END checking if sig is valid
  return true; // if compleats all tests return true
}
    unsigned char *LAMPORT::createsig(valtype *pdata, uint512_t *pprikey, int sellectedpubkey)
    {
      /* the signing will happen under this */
      valtype data = *pdata;
      uint512_t prikey = *pprikey;
      valtype pkey[20][2][20];
      unsigned char * hashabledata = new unsigned char[data.size()];

      valtype hash;
      CRIPEMD160().Write(&(hashabledata), sizeof(hashabledata)).Finalize(&(hash));

      return &sig[0][0][0];
    }
<|endoftext|>


<commit_before>/**
*  Copyright (C) 2020 3D Repo Ltd
*
*  This program is free software: you can redistribute it and/or modify
*  it under the terms of the GNU Affero General Public License as
*  published by the Free Software Foundation, either version 3 of the
*  License, or (at your option) any later version.
*
*  This program is distributed in the hope that it will be useful,
*  but WITHOUT ANY WARRANTY; without even the implied warranty of
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*  GNU Affero General Public License for more details.
*
*  You should have received a copy of the GNU Affero General Public License
*  along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <gtest/gtest.h>
#include <repo/manipulator/modelconvertor/import/repo_model_import_3drepo.h>
#include <repo/lib/repo_log.h>
#include "../../../../repo_test_utils.h"
#include "../../../../repo_test_database_info.h"
#include "boost/filesystem.hpp"

using namespace repo::manipulator::modelconvertor;
using namespace repo::test;

static bool testBIMFileImport(
	std::string bimFilePath,
	int expMaterialsCount,
	int expTexturesCount,
	int expMeshesCount,
	int expImpModelErrCode = REPOERR_OK,
	int expGenSceneErrCode = REPOERR_OK,
	bool generateScene = true,
	std::string debugMeshDataFilePath = "",
	std::string textureFilesDumpPath = "")
{
	ModelImportConfig config;
	uint8_t impModelErrCode = 0;
	auto modelConvertor = std::unique_ptr<AbstractModelImport>(new RepoModelImport(config));
	modelConvertor->importModel(bimFilePath, impModelErrCode);
	if (impModelErrCode != expImpModelErrCode)
	{
		repoInfo << "Error from importModel(): " << TestLogging::getStringFromRepoErrorCode((int)impModelErrCode);
		return false;
	}

	if(generateScene)
	{
		uint8_t genSceneErrCode = 0;
		auto repoScene = modelConvertor->generateRepoScene(genSceneErrCode);
		if (genSceneErrCode != expGenSceneErrCode)
		{
			repoInfo << "Error from generateRepoScene(): " << TestLogging::getStringFromRepoErrorCode((int)genSceneErrCode);
			return false;
		}

		auto materials = repoScene->getAllMaterials(repo::core::model::RepoScene::GraphType::DEFAULT);
		auto textures = repoScene->getAllTextures(repo::core::model::RepoScene::GraphType::DEFAULT);
		auto meshes = repoScene->getAllMeshes(repo::core::model::RepoScene::GraphType::DEFAULT);

		if(debugMeshDataFilePath != "")
		{
			// Print out the mesh debug text file
			std::ofstream stream;
			stream.open(debugMeshDataFilePath);
			if (stream)
			{
				for (auto const mesh : meshes)
				{
					auto meshNode = static_cast<repo::core::model::MeshNode*>(mesh);
					std::vector<repo_face_t> triangularFaces = meshNode->getFaces();
					std::vector<repo::lib::RepoVector3D> vertices = meshNode->getVertices();
					std::vector<repo::lib::RepoVector3D> normals = meshNode->getNormals();
					std::vector<repo::lib::RepoVector2D> uvs = meshNode->getUVChannels();
					char lastsep = ',';
					stream << "------mesh------" << std::endl;
					// Set this to the parent node id for an easier debugging life
					stream << mesh->getName() << std::endl;
					for (auto vertIt = vertices.begin(); vertIt != vertices.end(); ++vertIt)
					{
						if (vertIt == (vertices.end() - 1)) { lastsep = '\n'; }
						stream << vertIt->x << ",";
						stream << vertIt->y << ",";
						stream << vertIt->z << lastsep;
					}
					lastsep = ',';
					for (auto normIt = normals.begin(); normIt != normals.end(); ++normIt)
					{
						if (normIt == (normals.end() - 1)) { lastsep = '\n'; }
						stream << normIt->x << ",";
						stream << normIt->y << ",";
						stream << normIt->z << lastsep;
					}
					lastsep = ',';
					for (auto faceIt = triangularFaces.begin(); faceIt != triangularFaces.end(); ++faceIt)
					{
						if (faceIt == (triangularFaces.end() - 1)) { lastsep = '\n'; }
						stream << faceIt->at(0) << ",";
						stream << faceIt->at(1) << ",";
						stream << faceIt->at(2) << lastsep;
					}
					lastsep = ',';
					for (auto uvIt = uvs.begin(); uvIt != uvs.end(); ++uvIt)
					{
						if (uvIt== (uvs.end() - 1)) { lastsep = '\n'; }
						stream << uvIt->x << ",";
						stream << uvIt->y << lastsep;
					}
				}
			}
			stream.close();
		}

		if(textureFilesDumpPath != "")
		{
			for (auto const img : textures)
			{
				auto textureNode = static_cast<repo::core::model::TextureNode*>(img);
				std::vector<char> imgData = textureNode->getRawData();
				boost::filesystem::path dirPath(textureFilesDumpPath);
				boost::filesystem::path fileName( "DUMP_" + textureNode->getName());
				auto fullPath = dirPath / fileName;
				std::ofstream stream;
				stream.open(fullPath.string(), std::ios::binary);
				if (stream)
				{
					stream.write(&imgData[0], imgData.size());
				}
				stream.close();
			}
		}

		bool materialsOk = materials.size() == expMaterialsCount;
		if (!materialsOk) { repoInfo << "Expected " << expMaterialsCount << " materials, found " << materials.size(); }
		bool texturesOk = textures.size() == expTexturesCount;
		if (!materialsOk) { repoInfo << "Expected " << expTexturesCount << " textures, found " << textures.size(); }
		bool meshesOk = meshes.size() == expMeshesCount;
		if (!materialsOk) { repoInfo << "Expected " << expMeshesCount << " meshes, found " << meshes.size(); }
		if (!repoScene->isOK()) { repoInfo << "Scene is not healthy"; }

		bool scenePassed = materialsOk && texturesOk && meshesOk && repoScene->isOK();
		repoInfo << "Generated scene passed: " << std::boolalpha << scenePassed;
		return scenePassed;
	}

	return true;
};

TEST(RepoModelImport, SupportedFormats)
{
	TestLogging::printTestTitleString(
		"Supported BIM format tests",
		"Happy path tests to check for compatability with all of the supported BIM \
		file format versions");

	TestLogging::printSubTestTitleString("BIM003 file - with textures");
	EXPECT_TRUE(testBIMFileImport(
		getDataPath("RepoModelImport\\cube_bim3_revit_2021_repo_17de4e0.bim"), 4, 3, 4));

	TestLogging::printSubTestTitleString("BIM002 file");
	EXPECT_TRUE(testBIMFileImport(
		getDataPath("RepoModelImport\\cube_bim2_navis_2021_repo_4.6.1.bim"), 3, 0, 4));
}

TEST(RepoModelImport, UnsupportedFormats)
{
	TestLogging::printTestTitleString(
		"Unsupported BIM file format",
		"Checking to see if the right errors get thrown");

	TestLogging::printSubTestTitleString("BIM001 file - testing for unsupported error");
	EXPECT_TRUE(testBIMFileImport(
		getDataPath("RepoModelImport\\cube_bim1_spoofed.bim"), 0, 0, 0, REPOERR_UNSUPPORTED_BIM_VERSION, REPOERR_OK, false));
}

TEST(RepoModelImport, MissingTextureFields)
{
	TestLogging::printTestTitleString(
		"Texture fields missing in the BIM JSON",
		"Check to see if the correct error is generated");

	TestLogging::printSubTestTitleString("Missing \"numImageBytes\" field");
	EXPECT_FALSE(testBIMFileImport(
		getDataPath("RepoModelImport\\cube_bim3_corrupted_texture_field.bim"), 4, 2, 4, REPOERR_OK, REPOERR_LOAD_SCENE_MISSING_TEXTURE));
}

TEST(RepoModelImport, MissingReferencedTexture)
{
	TestLogging::printTestTitleString(
		"Materials references a missing texture",
		"Check to see if the correct error is generated");

	TestLogging::printSubTestTitleString("Material references a texture id not included in file");
	EXPECT_FALSE(testBIMFileImport(
		getDataPath("RepoModelImport\\cube_bim3_missing_texture.bim"), 4, 3, 4, REPOERR_OK, REPOERR_LOAD_SCENE_MISSING_TEXTURE));
}

<commit_msg>ISSUE #391 Updating unit test with the new UV channel changes. Adapting paths for compatability with Unix.<commit_after>/**
*  Copyright (C) 2020 3D Repo Ltd
*
*  This program is free software: you can redistribute it and/or modify
*  it under the terms of the GNU Affero General Public License as
*  published by the Free Software Foundation, either version 3 of the
*  License, or (at your option) any later version.
*
*  This program is distributed in the hope that it will be useful,
*  but WITHOUT ANY WARRANTY; without even the implied warranty of
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*  GNU Affero General Public License for more details.
*
*  You should have received a copy of the GNU Affero General Public License
*  along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <gtest/gtest.h>
#include <repo/manipulator/modelconvertor/import/repo_model_import_3drepo.h>
#include <repo/lib/repo_log.h>
#include "../../../../repo_test_utils.h"
#include "../../../../repo_test_database_info.h"
#include "boost/filesystem.hpp"

using namespace repo::manipulator::modelconvertor;
using namespace repo::test;

static bool testBIMFileImport(
	std::string bimFilePath,
	int expMaterialsCount,
	int expTexturesCount,
	int expMeshesCount,
	bool checkUvs = false,
	int expImpModelErrCode = REPOERR_OK,
	int expGenSceneErrCode = REPOERR_OK,
	bool generateScene = true,
	std::string debugMeshDataFilePath = "",
	std::string textureFilesDumpPath = "")
{
	ModelImportConfig config;
	uint8_t impModelErrCode = 0;
	auto modelConvertor = std::unique_ptr<AbstractModelImport>(new RepoModelImport(config));
	modelConvertor->importModel(bimFilePath, impModelErrCode);
	if (impModelErrCode != expImpModelErrCode)
	{
		repoError << "Error from importModel(): " << TestLogging::getStringFromRepoErrorCode((int)impModelErrCode);
		return false;
	}

	if(generateScene)
	{
		uint8_t genSceneErrCode = 0;
		auto repoScene = modelConvertor->generateRepoScene(genSceneErrCode);
		if (genSceneErrCode != expGenSceneErrCode)
		{
			repoError << "Error from generateRepoScene(): " << TestLogging::getStringFromRepoErrorCode((int)genSceneErrCode);
			return false;
		}

		auto materials = repoScene->getAllMaterials(repo::core::model::RepoScene::GraphType::DEFAULT);
		auto textures = repoScene->getAllTextures(repo::core::model::RepoScene::GraphType::DEFAULT);
		auto meshes = repoScene->getAllMeshes(repo::core::model::RepoScene::GraphType::DEFAULT);

		if(checkUvs)
		{
			for (auto const mesh : meshes)
			{
				auto meshNode = static_cast<repo::core::model::MeshNode*>(mesh);
				std::vector<repo::lib::RepoVector2D> uvs = meshNode->getUVChannels();
				std::vector<repo::lib::RepoVector3D> vertices = meshNode->getVertices();
				if(uvs.size() != vertices.size())
				{
					repoError << "UV count mesh is incorrect. Expected " << vertices.size() << ", found : " << uvs.size();
					return false;
				}
			}
		}

		if(debugMeshDataFilePath != "")
		{
			// Print out the mesh debug text file
			std::ofstream stream;
			stream.open(debugMeshDataFilePath);
			if (stream)
			{
				for (auto const mesh : meshes)
				{
					auto meshNode = static_cast<repo::core::model::MeshNode*>(mesh);
					std::vector<repo_face_t> triangularFaces = meshNode->getFaces();
					std::vector<repo::lib::RepoVector3D> vertices = meshNode->getVertices();
					std::vector<repo::lib::RepoVector3D> normals = meshNode->getNormals();
					std::vector<repo::lib::RepoVector2D> uvs = meshNode->getUVChannels();
					char lastsep = ',';
					stream << "------mesh------" << std::endl;
					// Set this to the parent node id for an easier debugging life
					stream << mesh->getName() << std::endl;
					for (auto vertIt = vertices.begin(); vertIt != vertices.end(); ++vertIt)
					{
						if (vertIt == (vertices.end() - 1)) { lastsep = '\n'; }
						stream << vertIt->x << ",";
						stream << vertIt->y << ",";
						stream << vertIt->z << lastsep;
					}
					lastsep = ',';
					for (auto normIt = normals.begin(); normIt != normals.end(); ++normIt)
					{
						if (normIt == (normals.end() - 1)) { lastsep = '\n'; }
						stream << normIt->x << ",";
						stream << normIt->y << ",";
						stream << normIt->z << lastsep;
					}
					lastsep = ',';
					for (auto faceIt = triangularFaces.begin(); faceIt != triangularFaces.end(); ++faceIt)
					{
						if (faceIt == (triangularFaces.end() - 1)) { lastsep = '\n'; }
						stream << faceIt->at(0) << ",";
						stream << faceIt->at(1) << ",";
						stream << faceIt->at(2) << lastsep;
					}
					lastsep = ',';
					for (auto uvIt = uvs.begin(); uvIt != uvs.end(); ++uvIt)
					{
						if (uvIt== (uvs.end() - 1)) { lastsep = '\n'; }
						stream << uvIt->x << ",";
						stream << uvIt->y << lastsep;
					}
				}
			}
			stream.close();
		}

		if(textureFilesDumpPath != "")
		{
			for (auto const img : textures)
			{
				auto textureNode = static_cast<repo::core::model::TextureNode*>(img);
				std::vector<char> imgData = textureNode->getRawData();
				boost::filesystem::path dirPath(textureFilesDumpPath);
				boost::filesystem::path fileName( "DUMP_" + textureNode->getName());
				auto fullPath = dirPath / fileName;
				std::ofstream stream;
				stream.open(fullPath.string(), std::ios::binary);
				if (stream)
				{
					stream.write(&imgData[0], imgData.size());
				}
				stream.close();
			}
		}

		bool materialsOk = materials.size() == expMaterialsCount;
		if (!materialsOk) { repoError << "Expected " << expMaterialsCount << " materials, found " << materials.size(); }
		bool texturesOk = textures.size() == expTexturesCount;
		if (!materialsOk) { repoError << "Expected " << expTexturesCount << " textures, found " << textures.size(); }
		bool meshesOk = meshes.size() == expMeshesCount;
		if (!materialsOk) { repoError << "Expected " << expMeshesCount << " meshes, found " << meshes.size(); }
		if (!repoScene->isOK()) { repoError << "Scene is not healthy"; }

		bool scenePassed = materialsOk && texturesOk && meshesOk && repoScene->isOK();
		repoInfo << "Generated scene passed: " << std::boolalpha << scenePassed;
		return scenePassed;
	}

	return true;
};

TEST(RepoModelImport, SupportedFormats)
{
	TestLogging::printTestTitleString(
		"Supported BIM format tests",
		"Happy path tests to check for compatability with all of the supported BIM \
		file format versions");

	TestLogging::printSubTestTitleString("BIM003 file - with textures");
	EXPECT_TRUE(testBIMFileImport(
		getDataPath("RepoModelImport/BrickWalls_bim3.bim"), 3, 2, 6, true));

	TestLogging::printSubTestTitleString("BIM002 file");
	EXPECT_TRUE(testBIMFileImport(
		getDataPath("RepoModelImport/cube_bim2_navis_2021_repo_4.6.1.bim"), 3, 0, 4));
}

TEST(RepoModelImport, UnsupportedFormats)
{
	TestLogging::printTestTitleString(
		"Unsupported BIM file format",
		"Checking to see if the right errors get thrown");

	TestLogging::printSubTestTitleString("BIM001 file - testing for unsupported error");
	EXPECT_TRUE(testBIMFileImport(
		getDataPath("RepoModelImport/cube_bim1_spoofed.bim"), 0, 0, 0, false, REPOERR_UNSUPPORTED_BIM_VERSION, REPOERR_OK, false));
}

TEST(RepoModelImport, MissingTextureFields)
{
	TestLogging::printTestTitleString(
		"Texture fields missing in the BIM JSON",
		"Check to see if the correct error is generated");

	TestLogging::printSubTestTitleString("Missing \"numImageBytes\" field");
	EXPECT_FALSE(testBIMFileImport(
		getDataPath("RepoModelImport/BrickWalls_bim3_CorruptedTextureField.bim"), 3, 1, 6, true, REPOERR_OK, REPOERR_LOAD_SCENE_MISSING_TEXTURE));
}

TEST(RepoModelImport, MissingReferencedTexture)
{
	TestLogging::printTestTitleString(
		"Materials references a missing texture",
		"Check to see if the correct error is generated");

	TestLogging::printSubTestTitleString("Material references a texture id not included in file");
	EXPECT_FALSE(testBIMFileImport(
		getDataPath("RepoModelImport/BrickWalls_bim3_MissingTexture.bim"), 3, 2, 6, true, REPOERR_OK, REPOERR_LOAD_SCENE_MISSING_TEXTURE));
}

<|endoftext|>


<commit_before>#pragma once

#include "util/rapidjson-helpers.hpp"
#include "util/serialize-custom.hpp"

#include <QRegularExpression>
#include <QString>
#include <memory>
#include <pajlada/settings/serialize.hpp>

namespace chatterino {
namespace controllers {
namespace highlights {

class HighlightPhrase
{
    QString pattern;
    bool alert;
    bool sound;
    bool _isRegex;
    QRegularExpression regex;

public:
    bool operator==(const HighlightPhrase &other) const
    {
        return std::tie(this->pattern, this->sound, this->alert, this->_isRegex) ==
               std::tie(other.pattern, other.sound, other.alert, other._isRegex);
    }

    HighlightPhrase(const QString &_pattern, bool _alert, bool _sound, bool isRegex)
        : pattern(_pattern)
        , alert(_alert)
        , sound(_sound)
        , _isRegex(isRegex)
        , regex(_isRegex ? _pattern : "\\b" + QRegularExpression::escape(_pattern) + "\\b",
                QRegularExpression::CaseInsensitiveOption)
    {
    }

    const QString &getPattern() const
    {
        return this->pattern;
    }
    bool getAlert() const
    {
        return this->alert;
    }
    bool getSound() const
    {
        return this->sound;
    }
    bool isRegex() const
    {
        return this->_isRegex;
    }

    bool isValid() const
    {
        return !this->pattern.isEmpty() && this->regex.isValid();
    }

    bool isMatch(const QString &subject) const
    {
        return this->isValid() && this->regex.match(subject).hasMatch();
    }

    //    const QRegularExpression &getRegex() const
    //    {
    //        return this->regex;
    //    }
};
}  // namespace highlights
}  // namespace controllers
}  // namespace chatterino

namespace pajlada {
namespace Settings {

template <>
struct Serialize<chatterino::controllers::highlights::HighlightPhrase> {
    static rapidjson::Value get(const chatterino::controllers::highlights::HighlightPhrase &value,
                                rapidjson::Document::AllocatorType &a)
    {
        rapidjson::Value ret(rapidjson::kObjectType);

        AddMember(ret, "pattern", value.getPattern(), a);
        AddMember(ret, "alert", value.getAlert(), a);
        AddMember(ret, "sound", value.getSound(), a);
        AddMember(ret, "regex", value.isRegex(), a);

        return ret;
    }
};

template <>
struct Deserialize<chatterino::controllers::highlights::HighlightPhrase> {
    static chatterino::controllers::highlights::HighlightPhrase get(const rapidjson::Value &value)
    {
        if (!value.IsObject()) {
            return chatterino::controllers::highlights::HighlightPhrase(QString(), true, false,
                                                                        false);
        }

        QString _pattern;
        bool _alert = true;
        bool _sound = false;
        bool _isRegex = false;

        chatterino::rj::getSafe(value, "pattern", _pattern);
        chatterino::rj::getSafe(value, "alert", _alert);
        chatterino::rj::getSafe(value, "sound", _sound);
        chatterino::rj::getSafe(value, "regex", _isRegex);

        return chatterino::controllers::highlights::HighlightPhrase(_pattern, _alert, _sound,
                                                                    _isRegex);
    }
};

}  // namespace Settings
}  // namespace pajlada
<commit_msg>Enable full unicode support in Highlight phrases<commit_after>#pragma once

#include "util/rapidjson-helpers.hpp"
#include "util/serialize-custom.hpp"

#include <QRegularExpression>
#include <QString>
#include <memory>
#include <pajlada/settings/serialize.hpp>

namespace chatterino {
namespace controllers {
namespace highlights {

class HighlightPhrase
{
    QString pattern;
    bool alert;
    bool sound;
    bool _isRegex;
    QRegularExpression regex;

public:
    bool operator==(const HighlightPhrase &other) const
    {
        return std::tie(this->pattern, this->sound, this->alert, this->_isRegex) ==
               std::tie(other.pattern, other.sound, other.alert, other._isRegex);
    }

    HighlightPhrase(const QString &_pattern, bool _alert, bool _sound, bool isRegex)
        : pattern(_pattern)
        , alert(_alert)
        , sound(_sound)
        , _isRegex(isRegex)
        , regex(_isRegex ? _pattern : "\\b" + QRegularExpression::escape(_pattern) + "\\b",
                QRegularExpression::CaseInsensitiveOption |
                    QRegularExpression::UseUnicodePropertiesOption)
    {
    }

    const QString &getPattern() const
    {
        return this->pattern;
    }
    bool getAlert() const
    {
        return this->alert;
    }
    bool getSound() const
    {
        return this->sound;
    }
    bool isRegex() const
    {
        return this->_isRegex;
    }

    bool isValid() const
    {
        return !this->pattern.isEmpty() && this->regex.isValid();
    }

    bool isMatch(const QString &subject) const
    {
        return this->isValid() && this->regex.match(subject).hasMatch();
    }

    //    const QRegularExpression &getRegex() const
    //    {
    //        return this->regex;
    //    }
};
}  // namespace highlights
}  // namespace controllers
}  // namespace chatterino

namespace pajlada {
namespace Settings {

template <>
struct Serialize<chatterino::controllers::highlights::HighlightPhrase> {
    static rapidjson::Value get(const chatterino::controllers::highlights::HighlightPhrase &value,
                                rapidjson::Document::AllocatorType &a)
    {
        rapidjson::Value ret(rapidjson::kObjectType);

        AddMember(ret, "pattern", value.getPattern(), a);
        AddMember(ret, "alert", value.getAlert(), a);
        AddMember(ret, "sound", value.getSound(), a);
        AddMember(ret, "regex", value.isRegex(), a);

        return ret;
    }
};

template <>
struct Deserialize<chatterino::controllers::highlights::HighlightPhrase> {
    static chatterino::controllers::highlights::HighlightPhrase get(const rapidjson::Value &value)
    {
        if (!value.IsObject()) {
            return chatterino::controllers::highlights::HighlightPhrase(QString(), true, false,
                                                                        false);
        }

        QString _pattern;
        bool _alert = true;
        bool _sound = false;
        bool _isRegex = false;

        chatterino::rj::getSafe(value, "pattern", _pattern);
        chatterino::rj::getSafe(value, "alert", _alert);
        chatterino::rj::getSafe(value, "sound", _sound);
        chatterino::rj::getSafe(value, "regex", _isRegex);

        return chatterino::controllers::highlights::HighlightPhrase(_pattern, _alert, _sound,
                                                                    _isRegex);
    }
};

}  // namespace Settings
}  // namespace pajlada
<|endoftext|>


<commit_before>/*
 * SessionViewPdf.cpp
 *
 * Copyright (C) 2009-12 by RStudio, Inc.
 *
 * Unless you have received this program directly from RStudio pursuant
 * to the terms of a commercial license agreement with RStudio, then
 * this program is licensed to you under the terms of version 3 of the
 * GNU Affero General Public License. This program is distributed WITHOUT
 * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
 * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
 *
 */

#include "SessionViewPdf.hpp"

#include <core/Error.hpp>
#include <core/FilePath.hpp>
#include <core/Exec.hpp>

#include <core/http/Util.hpp>

#include <session/SessionModuleContext.hpp>

#define kPdfJsPath "/pdf_js/"

using namespace core;

namespace session {
namespace modules {
namespace tex {
namespace view_pdf {

namespace {

void handleViewPdf(const http::Request& request, http::Response* pResponse)
{
   // get the file path
   FilePath filePath(request.queryParamValue("path"));
   if (!filePath.exists())
   {
      pResponse->setError(http::status::NotFound, "File not found");
      return;
   }

   // send it back
   pResponse->setNoCacheHeaders();
   pResponse->setFile(filePath, request);
   pResponse->setContentType("application/pdf");
}

void handlePdfJs(const http::Request& request, http::Response* pResponse)
{
   std::string path("pdfjs/");
   path.append(http::util::pathAfterPrefix(request, kPdfJsPath));

   core::FilePath pdfJsResource = options().rResourcesPath().childPath(path);
   if (pdfJsResource.exists())
   {
      pResponse->setFile(pdfJsResource, request);
      return;
   }
}

} // anonymous namespace

std::string createViewPdfUrl(const core::FilePath& filePath)
{
   return "view_pdf?path=" + http::util::urlEncode(filePath.absolutePath(),
                                                   true);
}

Error initialize()
{
   // install rpc methods
   using boost::bind;
   using namespace module_context;
   ExecBlock initBlock ;
   initBlock.addFunctions()
      (bind(registerUriHandler, "/view_pdf", handleViewPdf))
      (bind(registerUriHandler, kPdfJsPath, handlePdfJs))
   ;
   return initBlock.execute();
}

} // namespace view_pdf
} // namespace tex
} // namespace modules
} // namesapce session

<commit_msg>guard against requesting arbitrary PDF files from hosted viewer<commit_after>/*
 * SessionViewPdf.cpp
 *
 * Copyright (C) 2009-12 by RStudio, Inc.
 *
 * Unless you have received this program directly from RStudio pursuant
 * to the terms of a commercial license agreement with RStudio, then
 * this program is licensed to you under the terms of version 3 of the
 * GNU Affero General Public License. This program is distributed WITHOUT
 * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
 * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
 *
 */

#include "SessionViewPdf.hpp"

#include <core/Error.hpp>
#include <core/FilePath.hpp>
#include <core/Exec.hpp>

#include <core/http/Util.hpp>

#include <session/SessionModuleContext.hpp>

#define kPdfJsPath "/pdf_js/"

using namespace core;

namespace session {
namespace modules {
namespace tex {
namespace view_pdf {

namespace {

void handleViewPdf(const http::Request& request, http::Response* pResponse)
{
   // get the file path
   FilePath filePath(request.queryParamValue("path"));
   if (!filePath.exists())
   {
      pResponse->setError(http::status::NotFound, "File not found");
      return;
   }

   // send it back
   pResponse->setNoCacheHeaders();
   pResponse->setFile(filePath, request);
   pResponse->setContentType("application/pdf");
}

void handlePdfJs(const http::Request& request, http::Response* pResponse)
{
   std::string path("pdfjs/");
   path.append(http::util::pathAfterPrefix(request, kPdfJsPath));
   if (request.queryString().find("file=") != std::string::npos &&
       request.queryString() != "file=")
   {
      // when a file is specified, we expect it to be empty; deny requests for
      // specific files (we don't want arbitrary URLs to be loaded)
      pResponse->setError(
               http::status::InternalServerError,
               "Incorrect parameters");
      return;
   }

   core::FilePath pdfJsResource = options().rResourcesPath().childPath(path);
   if (pdfJsResource.exists())
   {
      pResponse->setFile(pdfJsResource, request);
      return;
   }
}

} // anonymous namespace

std::string createViewPdfUrl(const core::FilePath& filePath)
{
   return "view_pdf?path=" + http::util::urlEncode(filePath.absolutePath(),
                                                   true);
}

Error initialize()
{
   // install rpc methods
   using boost::bind;
   using namespace module_context;
   ExecBlock initBlock ;
   initBlock.addFunctions()
      (bind(registerUriHandler, "/view_pdf", handleViewPdf))
      (bind(registerUriHandler, kPdfJsPath, handlePdfJs))
   ;
   return initBlock.execute();
}

} // namespace view_pdf
} // namespace tex
} // namespace modules
} // namesapce session

<|endoftext|>


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

#include "Stdafx.h"
#include "CefBrowserHostWrapper.h"
#include "CefPdfPrintCallbackWrapper.h"
#include "WindowInfo.h"

void CefBrowserHostWrapper::StartDownload(String^ url)
{
    ThrowIfDisposed();

    _browserHost->StartDownload(StringUtils::ToNative(url));
}

void CefBrowserHostWrapper::Print()
{
    ThrowIfDisposed();

    _browserHost->Print();
}

void CefBrowserHostWrapper::PrintToPDF(String^ path, CefSharpPdfPrintSettings^ settings, IPrintToPdfCallback^ callback)
{
    CefPdfPrintSettings nativeSettings;
    CefString(&nativeSettings.header_footer_title).FromString(StringUtils::ToNative(settings->HeaderFooterTitle));
    CefString(&nativeSettings.header_footer_url).FromString(StringUtils::ToNative(settings->HeaderFooterUrl));
    nativeSettings.backgrounds_enabled = settings->BackgroundsEnabled ? 1 : 0;
    nativeSettings.header_footer_enabled = settings->HeaderFooterEnabled ? 1 : 0;
    nativeSettings.landscape = settings->Landscape ? 1 : 0;
    nativeSettings.selection_only = settings->SelectionOnly ? 1 : 0;
    nativeSettings.margin_bottom = settings->MarginBottom;
    nativeSettings.margin_top = settings->MarginTop;
    nativeSettings.margin_left= settings->MarginLeft;
    nativeSettings.margin_right = settings->MarginRight;
    nativeSettings.page_height = settings->PageHeight;
    nativeSettings.page_width = settings->PageWidth;
    switch (settings->MarginType)
    {
    case CefPdfPrintMarginType::Default:
        nativeSettings.margin_type = PDF_PRINT_MARGIN_DEFAULT;
        break;
    case CefPdfPrintMarginType::Minimum:
        nativeSettings.margin_type = PDF_PRINT_MARGIN_MINIMUM;
        break;
    case CefPdfPrintMarginType::None:
        nativeSettings.margin_type = PDF_PRINT_MARGIN_NONE;
        break;
    case CefPdfPrintMarginType::Custom:
        nativeSettings.margin_type = PDF_PRINT_MARGIN_CUSTOM;
        break;
    }

    _browserHost->PrintToPDF(StringUtils::ToNative(path), nativeSettings, new CefPdfPrintCallbackWrapper(callback));
}

void CefBrowserHostWrapper::SetZoomLevel(double zoomLevel)
{
    ThrowIfDisposed();

    _browserHost->SetZoomLevel(zoomLevel);
}

Task<double>^ CefBrowserHostWrapper::GetZoomLevelAsync()
{
    ThrowIfDisposed();

    if (CefCurrentlyOn(TID_UI))
    {
        TaskCompletionSource<double>^ taskSource = gcnew TaskCompletionSource<double>();
        taskSource->SetResult(GetZoomLevelOnUI());
        return taskSource->Task;
    }
    return Cef::UIThreadTaskFactory->StartNew(gcnew Func<double>(this, &CefBrowserHostWrapper::GetZoomLevelOnUI));
}

IntPtr CefBrowserHostWrapper::GetWindowHandle()
{
    ThrowIfDisposed();

    return IntPtr(_browserHost->GetWindowHandle());
}

void CefBrowserHostWrapper::CloseBrowser(bool forceClose)
{
    ThrowIfDisposed();

    _browserHost->CloseBrowser(forceClose);
}

void CefBrowserHostWrapper::ShowDevTools(IWindowInfo^ windowInfo, int inspectElementAtX, int inspectElementAtY)
{
    ThrowIfDisposed();

    CefBrowserSettings settings;
    CefWindowInfo nativeWindowInfo;

    if(windowInfo == nullptr)
    {
        nativeWindowInfo.SetAsPopup(_browserHost->GetWindowHandle(), "DevTools");
    }
    else
    {
        auto cefWindowInfoWrapper = static_cast<WindowInfo^>(windowInfo);

        nativeWindowInfo = *cefWindowInfoWrapper->GetWindowInfo();
    }

    _browserHost->ShowDevTools(nativeWindowInfo, _browserHost->GetClient(), settings, CefPoint(inspectElementAtX, inspectElementAtY));
}

void CefBrowserHostWrapper::CloseDevTools()
{
    ThrowIfDisposed();

    _browserHost->CloseDevTools();
}

void CefBrowserHostWrapper::AddWordToDictionary(String^ word)
{
    ThrowIfDisposed();

    _browserHost->AddWordToDictionary(StringUtils::ToNative(word));
}

void CefBrowserHostWrapper::ReplaceMisspelling(String^ word)
{
    ThrowIfDisposed();

    _browserHost->ReplaceMisspelling(StringUtils::ToNative(word));
}

void CefBrowserHostWrapper::Find(int identifier, String^ searchText, bool forward, bool matchCase, bool findNext)
{
    ThrowIfDisposed();

    _browserHost->Find(identifier, StringUtils::ToNative(searchText), forward, matchCase, findNext);
}

void CefBrowserHostWrapper::StopFinding(bool clearSelection)
{
    ThrowIfDisposed();

    _browserHost->StopFinding(clearSelection);
}

void CefBrowserHostWrapper::SetFocus(bool focus)
{
    ThrowIfDisposed();

    _browserHost->SetFocus(focus);
}

void CefBrowserHostWrapper::SendFocusEvent(bool setFocus)
{
    ThrowIfDisposed();

    _browserHost->SendFocusEvent(setFocus);
}

void CefBrowserHostWrapper::SendKeyEvent(KeyEvent keyEvent)
{
    CefKeyEvent nativeKeyEvent;
    nativeKeyEvent.focus_on_editable_field = keyEvent.FocusOnEditableField == 1;
    nativeKeyEvent.is_system_key = keyEvent.IsSystemKey == 1;
    nativeKeyEvent.modifiers = (uint32)keyEvent.Modifiers;
    nativeKeyEvent.type = (cef_key_event_type_t)keyEvent.Type;
    nativeKeyEvent.native_key_code = keyEvent.NativeKeyCode;
    nativeKeyEvent.windows_key_code = keyEvent.WindowsKeyCode;

    _browserHost->SendKeyEvent(nativeKeyEvent);
}

double CefBrowserHostWrapper::GetZoomLevelOnUI()
{
    ThrowIfDisposed();

    CefTaskScheduler::EnsureOn(TID_UI, "CefBrowserHostWrapper::GetZoomLevel");

    //Don't throw exception if no browser host here as it's not easy to handle
    if(_browserHost.get())
    {
        return _browserHost->GetZoomLevel();
    }

    return 0.0;
}

void CefBrowserHostWrapper::SendMouseWheelEvent(int x, int y, int deltaX, int deltaY, CefEventFlags modifiers)
{
    ThrowIfDisposed();

    if (_browserHost.get())
    {
        CefMouseEvent mouseEvent;
        mouseEvent.x = x;
        mouseEvent.y = y;
        mouseEvent.modifiers = (uint32)modifiers;

        _browserHost->SendMouseWheelEvent(mouseEvent, deltaX, deltaY);
    }
}

void CefBrowserHostWrapper::Invalidate(PaintElementType type)
{
    ThrowIfDisposed();

    _browserHost->Invalidate((CefBrowserHost::PaintElementType)type);
}

void CefBrowserHostWrapper::SendMouseClickEvent(int x, int y, MouseButtonType mouseButtonType, bool mouseUp, int clickCount, CefEventFlags modifiers)
{
    ThrowIfDisposed();

    CefMouseEvent mouseEvent;
    mouseEvent.x = x;
    mouseEvent.y = y;
    mouseEvent.modifiers = (uint32)modifiers;

    _browserHost->SendMouseClickEvent(mouseEvent, (CefBrowserHost::MouseButtonType) mouseButtonType, mouseUp, clickCount);
}

void CefBrowserHostWrapper::SendMouseMoveEvent(int x, int y, bool mouseLeave, CefEventFlags modifiers)
{
    ThrowIfDisposed();

    CefMouseEvent mouseEvent;
    mouseEvent.x = x;
    mouseEvent.y = y;

    mouseEvent.modifiers = (uint32)modifiers;

    _browserHost->SendMouseMoveEvent(mouseEvent, mouseLeave);
}

int CefBrowserHostWrapper::WindowlessFrameRate::get()
{
    return _browserHost->GetWindowlessFrameRate();
}

void CefBrowserHostWrapper::WindowlessFrameRate::set(int val)
{
    _browserHost->SetWindowlessFrameRate(val);
}<commit_msg>simplified PrintToPDF in CefBrowserHostWrapper<commit_after>// Copyright  2010-2015 The CefSharp Authors. All rights reserved.
//
// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.

#include "Stdafx.h"
#include "CefBrowserHostWrapper.h"
#include "CefPdfPrintCallbackWrapper.h"
#include "WindowInfo.h"

void CefBrowserHostWrapper::StartDownload(String^ url)
{
    ThrowIfDisposed();

    _browserHost->StartDownload(StringUtils::ToNative(url));
}

void CefBrowserHostWrapper::Print()
{
    ThrowIfDisposed();

    _browserHost->Print();
}

void CefBrowserHostWrapper::PrintToPDF(String^ path, CefSharpPdfPrintSettings^ settings, IPrintToPdfCallback^ callback)
{
    CefPdfPrintSettings nativeSettings;
    StringUtils::AssignNativeFromClr(nativeSettings.header_footer_title, settings->HeaderFooterUrl);
    StringUtils::AssignNativeFromClr(nativeSettings.header_footer_url, settings->HeaderFooterTitle);
    nativeSettings.backgrounds_enabled = settings->BackgroundsEnabled ? 1 : 0;
    nativeSettings.header_footer_enabled = settings->HeaderFooterEnabled ? 1 : 0;
    nativeSettings.landscape = settings->Landscape ? 1 : 0;
    nativeSettings.selection_only = settings->SelectionOnly ? 1 : 0;
    nativeSettings.margin_bottom = settings->MarginBottom;
    nativeSettings.margin_top = settings->MarginTop;
    nativeSettings.margin_left= settings->MarginLeft;
    nativeSettings.margin_right = settings->MarginRight;
    nativeSettings.page_height = settings->PageHeight;
    nativeSettings.page_width = settings->PageWidth;
    nativeSettings.margin_type = static_cast<cef_pdf_print_margin_type_t>(settings->MarginType);

    _browserHost->PrintToPDF(StringUtils::ToNative(path), nativeSettings, new CefPdfPrintCallbackWrapper(callback));
}

void CefBrowserHostWrapper::SetZoomLevel(double zoomLevel)
{
    ThrowIfDisposed();

    _browserHost->SetZoomLevel(zoomLevel);
}

Task<double>^ CefBrowserHostWrapper::GetZoomLevelAsync()
{
    ThrowIfDisposed();

    if (CefCurrentlyOn(TID_UI))
    {
        TaskCompletionSource<double>^ taskSource = gcnew TaskCompletionSource<double>();
        taskSource->SetResult(GetZoomLevelOnUI());
        return taskSource->Task;
    }
    return Cef::UIThreadTaskFactory->StartNew(gcnew Func<double>(this, &CefBrowserHostWrapper::GetZoomLevelOnUI));
}

IntPtr CefBrowserHostWrapper::GetWindowHandle()
{
    ThrowIfDisposed();

    return IntPtr(_browserHost->GetWindowHandle());
}

void CefBrowserHostWrapper::CloseBrowser(bool forceClose)
{
    ThrowIfDisposed();

    _browserHost->CloseBrowser(forceClose);
}

void CefBrowserHostWrapper::ShowDevTools(IWindowInfo^ windowInfo, int inspectElementAtX, int inspectElementAtY)
{
    ThrowIfDisposed();

    CefBrowserSettings settings;
    CefWindowInfo nativeWindowInfo;

    if(windowInfo == nullptr)
    {
        nativeWindowInfo.SetAsPopup(_browserHost->GetWindowHandle(), "DevTools");
    }
    else
    {
        auto cefWindowInfoWrapper = static_cast<WindowInfo^>(windowInfo);

        nativeWindowInfo = *cefWindowInfoWrapper->GetWindowInfo();
    }

    _browserHost->ShowDevTools(nativeWindowInfo, _browserHost->GetClient(), settings, CefPoint(inspectElementAtX, inspectElementAtY));
}

void CefBrowserHostWrapper::CloseDevTools()
{
    ThrowIfDisposed();

    _browserHost->CloseDevTools();
}

void CefBrowserHostWrapper::AddWordToDictionary(String^ word)
{
    ThrowIfDisposed();

    _browserHost->AddWordToDictionary(StringUtils::ToNative(word));
}

void CefBrowserHostWrapper::ReplaceMisspelling(String^ word)
{
    ThrowIfDisposed();

    _browserHost->ReplaceMisspelling(StringUtils::ToNative(word));
}

void CefBrowserHostWrapper::Find(int identifier, String^ searchText, bool forward, bool matchCase, bool findNext)
{
    ThrowIfDisposed();

    _browserHost->Find(identifier, StringUtils::ToNative(searchText), forward, matchCase, findNext);
}

void CefBrowserHostWrapper::StopFinding(bool clearSelection)
{
    ThrowIfDisposed();

    _browserHost->StopFinding(clearSelection);
}

void CefBrowserHostWrapper::SetFocus(bool focus)
{
    ThrowIfDisposed();

    _browserHost->SetFocus(focus);
}

void CefBrowserHostWrapper::SendFocusEvent(bool setFocus)
{
    ThrowIfDisposed();

    _browserHost->SendFocusEvent(setFocus);
}

void CefBrowserHostWrapper::SendKeyEvent(KeyEvent keyEvent)
{
    CefKeyEvent nativeKeyEvent;
    nativeKeyEvent.focus_on_editable_field = keyEvent.FocusOnEditableField == 1;
    nativeKeyEvent.is_system_key = keyEvent.IsSystemKey == 1;
    nativeKeyEvent.modifiers = (uint32)keyEvent.Modifiers;
    nativeKeyEvent.type = (cef_key_event_type_t)keyEvent.Type;
    nativeKeyEvent.native_key_code = keyEvent.NativeKeyCode;
    nativeKeyEvent.windows_key_code = keyEvent.WindowsKeyCode;

    _browserHost->SendKeyEvent(nativeKeyEvent);
}

double CefBrowserHostWrapper::GetZoomLevelOnUI()
{
    ThrowIfDisposed();

    CefTaskScheduler::EnsureOn(TID_UI, "CefBrowserHostWrapper::GetZoomLevel");

    //Don't throw exception if no browser host here as it's not easy to handle
    if(_browserHost.get())
    {
        return _browserHost->GetZoomLevel();
    }

    return 0.0;
}

void CefBrowserHostWrapper::SendMouseWheelEvent(int x, int y, int deltaX, int deltaY, CefEventFlags modifiers)
{
    ThrowIfDisposed();

    if (_browserHost.get())
    {
        CefMouseEvent mouseEvent;
        mouseEvent.x = x;
        mouseEvent.y = y;
        mouseEvent.modifiers = (uint32)modifiers;

        _browserHost->SendMouseWheelEvent(mouseEvent, deltaX, deltaY);
    }
}

void CefBrowserHostWrapper::Invalidate(PaintElementType type)
{
    ThrowIfDisposed();

    _browserHost->Invalidate((CefBrowserHost::PaintElementType)type);
}

void CefBrowserHostWrapper::SendMouseClickEvent(int x, int y, MouseButtonType mouseButtonType, bool mouseUp, int clickCount, CefEventFlags modifiers)
{
    ThrowIfDisposed();

    CefMouseEvent mouseEvent;
    mouseEvent.x = x;
    mouseEvent.y = y;
    mouseEvent.modifiers = (uint32)modifiers;

    _browserHost->SendMouseClickEvent(mouseEvent, (CefBrowserHost::MouseButtonType) mouseButtonType, mouseUp, clickCount);
}

void CefBrowserHostWrapper::SendMouseMoveEvent(int x, int y, bool mouseLeave, CefEventFlags modifiers)
{
    ThrowIfDisposed();

    CefMouseEvent mouseEvent;
    mouseEvent.x = x;
    mouseEvent.y = y;

    mouseEvent.modifiers = (uint32)modifiers;

    _browserHost->SendMouseMoveEvent(mouseEvent, mouseLeave);
}

int CefBrowserHostWrapper::WindowlessFrameRate::get()
{
    return _browserHost->GetWindowlessFrameRate();
}

void CefBrowserHostWrapper::WindowlessFrameRate::set(int val)
{
    _browserHost->SetWindowlessFrameRate(val);
}<|endoftext|>


<commit_before>/*
 * Copyright © 2012 Intel Corporation
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
 *
 * Author: Benjamin Segovia <benjamin.segovia@intel.com>
 */

/**
 * \file gen_reg_allocation.hpp
 * \author Benjamin Segovia <benjamin.segovia@intel.com>
 */

#ifndef __GBE_GEN_REG_ALLOCATION_HPP__
#define __GBE_GEN_REG_ALLOCATION_HPP__

#include "ir/register.hpp"
#include "backend/gen_register.hpp"

namespace gbe
{
  class Selection;      // Pre-register allocation code generation
  class GenRegister;    // Pre-register allocation Gen register
  class GenRegInterval; // Liveness interval for each register
  class GenContext;     // Gen specific context

  /*! Register allocate (i.e. virtual to physical register mapping) */
  class GenRegAllocator
  {
  public:
    /*! Initialize the register allocator */
    GenRegAllocator(GenContext &ctx);
    /*! Release all taken resources */
    ~GenRegAllocator(void);
    /*! Perform the register allocation */
    bool allocate(Selection &selection);
    /*! Virtual to physical translation */
    GenRegister genReg(const GenRegister &reg);
    /*! Output the register allocation */
    void outputAllocation(void);
  private:
    /*! Actual implementation of the register allocator (use Pimpl) */
    class Opaque;
    /*! Created and destroyed in cpp */
    Opaque *opaque;
    /*! Use custom allocator */
    GBE_CLASS(GenRegAllocator);
  };

} /* namespace gbe */

#endif /* __GBE_GEN_REG_ALLOCATION_HPP__ */

<commit_msg>GenRegInterval should be a struct instead of a class<commit_after>/*
 * Copyright © 2012 Intel Corporation
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
 *
 * Author: Benjamin Segovia <benjamin.segovia@intel.com>
 */

/**
 * \file gen_reg_allocation.hpp
 * \author Benjamin Segovia <benjamin.segovia@intel.com>
 */

#ifndef __GBE_GEN_REG_ALLOCATION_HPP__
#define __GBE_GEN_REG_ALLOCATION_HPP__

#include "ir/register.hpp"
#include "backend/gen_register.hpp"

namespace gbe
{
  class Selection;      // Pre-register allocation code generation
  class GenRegister;    // Pre-register allocation Gen register
  struct GenRegInterval; // Liveness interval for each register
  class GenContext;     // Gen specific context

  /*! Register allocate (i.e. virtual to physical register mapping) */
  class GenRegAllocator
  {
  public:
    /*! Initialize the register allocator */
    GenRegAllocator(GenContext &ctx);
    /*! Release all taken resources */
    ~GenRegAllocator(void);
    /*! Perform the register allocation */
    bool allocate(Selection &selection);
    /*! Virtual to physical translation */
    GenRegister genReg(const GenRegister &reg);
    /*! Output the register allocation */
    void outputAllocation(void);
  private:
    /*! Actual implementation of the register allocator (use Pimpl) */
    class Opaque;
    /*! Created and destroyed in cpp */
    Opaque *opaque;
    /*! Use custom allocator */
    GBE_CLASS(GenRegAllocator);
  };

} /* namespace gbe */

#endif /* __GBE_GEN_REG_ALLOCATION_HPP__ */

<|endoftext|>


<commit_before>#include <cmath>
#include <QDateTime>
#include <QsLog.h>
#include <QTimer>
#include "ac_sensor.h"
#include "control_loop.h"
#include "multi.h"
#include "multi_phase_data.h"
#include "power_info.h"
#include "settings.h"

ControlLoop::ControlLoop(Multi *multi, Phase phase, AcSensor *AcSensor,
						 Settings *settings, Clock *clock, QObject *parent):
	QObject(parent),
	mMulti(multi),
	mAcSensor(AcSensor),
	mSettings(settings),
	mTimer(new QTimer(this)),
	mClock(clock),
	mPhase(phase),
	mMultiUpdate(false),
	mMeterUpdate(false)
{
	Q_ASSERT(multi != 0);
	Q_ASSERT(multi->getPhaseData(phase) != 0);
	Q_ASSERT(AcSensor != 0);
	Q_ASSERT(settings != 0);
	if (mClock == 0)
		mClock.reset(new DefaultClock());
	mTimer->setInterval(5000);
	mTimer->start();
	connect(mMulti, SIGNAL(destroyed()), this, SLOT(onDestroyed()));
	connect(mMulti->getPhaseData(mPhase), SIGNAL(acPowerOutChanged()),
			this, SLOT(onPowerFromMulti()));
	connect(mAcSensor, SIGNAL(destroyed()), this, SLOT(onDestroyed()));
	connect(mAcSensor->getPowerInfo(mPhase), SIGNAL(powerChanged()),
			this, SLOT(onPowerFromMeter()));
	connect(mTimer, SIGNAL(timeout()), this, SLOT(onTimer()));
}

Phase ControlLoop::phase() const
{
	return mPhase;
}

void ControlLoop::onDestroyed()
{
	deleteLater();
}

void ControlLoop::onTimer()
{
	QLOG_DEBUG() << "Update timeout";
	mMultiUpdate = true;
	mMeterUpdate = true;
	checkStep();
}

void ControlLoop::onPowerFromMulti()
{
	mMultiUpdate = true;
	checkStep();
}

void ControlLoop::onPowerFromMeter()
{
	mMeterUpdate = true;
	checkStep();
}

void ControlLoop::checkStep()
{
	if (!mMeterUpdate || !mMultiUpdate)
		return;
	performStep();
	mMeterUpdate = false;
	mMultiUpdate = false;
	mTimer->start();
}

void ControlLoop::performStep()
{
	// pMultiNew < 0: battery is discharging
	// pMultiNew > 0: battery is charging
	const double MaxMultiPower = 32700;
	const double MinMultiPower = -MaxMultiPower;
	double pMultiNew = 0;
	double maxChargePct = qMax(0.0, qMin(100.0, mSettings->maxChargePercentage()));
	double maxDischargePct = qMax(0.0, qMin(100.0, mSettings->maxDischargePercentage()));
	double maxPower = maxChargePct <= 99 ?
			maxChargePct *
			mMulti->maxChargeCurrent() *
			mMulti->dcVoltage() / 100:
			MaxMultiPower;

	if (mSettings->maintenanceInterval() == 0) {
		setHub4State(Hub4External);
	} else if (mSettings->state() == Hub4External) {
		setHub4State(Hub4SelfConsumption);
	}

	switch (mSettings->state()) {
	case Hub4SelfConsumption:
	{
		pMultiNew = computeSetpoint();
		QDateTime nextCharge = mSettings->maintenanceDate();
		if (!nextCharge.isValid()) {
			updateMaintenanceDate();
			nextCharge = mSettings->maintenanceDate();
		}
		nextCharge = nextCharge.addDays(mSettings->maintenanceInterval());
		QDateTime now = mClock->now();
		if (now >= nextCharge && now.time().hour() == 12) {
			setHub4State(Hub4ChargeFromGrid);
		}

		if (isMultiCharged()) {
			setHub4State(Hub4Charged);
		}
		break;
	}
	case Hub4Charged:
		pMultiNew = computeSetpoint();
		if (!isMultiCharged()) {
			setHub4State(Hub4SelfConsumption);
			updateMaintenanceDate();
		}
		break;
	case Hub4External:
		pMultiNew = computeSetpoint();
		break;
	case Hub4ChargeFromGrid:
		pMultiNew = maxPower;
		if (isMultiCharged()) {
			setHub4State(Hub4SelfConsumption);
			updateMaintenanceDate();
		}
		break;
	case Hub4Storage:
		pMultiNew = maxPower;
		if (isMultiCharged())
			mSettings->setMaintenanceDate(QDateTime());
		break;
	}

	bool feedbackDisabled = maxDischargePct < 50;
	// It seems that enabling the the ChargeDisabled flag disables both charge
	// and discharge. So we're only setting the flags when we are not
	// discharging.
	bool chargeDisabled = maxChargePct <= 0 &&
						  (feedbackDisabled || pMultiNew > 30);
	pMultiNew = qBound(MinMultiPower, pMultiNew, maxPower);

	// Ugly workaround: the value of pMultiNew must always be sent over the
	// D-Bus, even when it does not change, because the multi will reset its
	// power setpoint if no value has been set during the last 10 seconds.
	// So we add a random value to ensure pMultiNew changes. Other solution
	// would involve changes in velib (VBusItem and friends).
	pMultiNew -= qrand() / (10.0 * RAND_MAX);

	mMulti->setIsChargeDisabled(chargeDisabled);
	mMulti->setIsFeedbackDisabled(feedbackDisabled);
	// If feedback is disabled and the multi is 'on', it will start inverting
	// when the grid drops ways (emergency power). If we set the multi to
	// 'charge only', it will not start inverting so the system on AC-In and
	// AC-Out will lose power.
	// mMulti->setMode(feedbackDisabled ? MultiChargerOnly : MultiOn);
	QLOG_TRACE() << mAcSensor->getPowerInfo(mPhase)->power()
				 << '\t' << mMulti->getPhaseData(mPhase)->acPowerIn()
				 << '\t' << pMultiNew
				 << '\t' << chargeDisabled
				 << '\t' << feedbackDisabled;
	if (std::isfinite(pMultiNew))
		mMulti->setAcPowerSetPoint(pMultiNew);
}

double ControlLoop::computeSetpoint() const
{
	PowerInfo *pi = mAcSensor->getPowerInfo(mPhase);
	double pNet = pi->power();
	if (!std::isfinite(pNet))
		return NaN;
	MultiPhaseData *mpd = mMulti->getPhaseData(mPhase);
	double pMulti = mpd->acPowerIn();
	double pTarget = mSettings->acPowerSetPoint();
	// EV: EM340 seems to work better with Alpha = 0.6. Possibly due to lower
	// update rate.
	const double Alpha = 0.8;
	return pMulti + Alpha * (pTarget - pNet);
}

bool ControlLoop::isMultiCharged() const
{
	return mMulti->state() == MultiStateFloat || mMulti->state() == MultiStateStorage;
}

void ControlLoop::updateMaintenanceDate()
{
	QDateTime nextCharge = mClock->now();
	nextCharge.setTime(QTime(1, 0, 0));
	mSettings->setMaintenanceDate(nextCharge);
	QLOG_INFO() << "Next maintenance base date set at:" << nextCharge;
}

void ControlLoop::setHub4State(Hub4State state)
{
	if (mSettings->state() == state)
		return;
	QLOG_INFO() << "Changing Hub4 state from"
				<< getStateName(mSettings->state())
				<< "to" << getStateName(state);
	mSettings->setState(state);
}

const char *ControlLoop::getStateName(int state)
{
	static const char *StateNames[] = { "SelfConsumption", "ChargeFromGrid", "Charged", "Storage", "External" };
	static const int StateNameCount	= static_cast<int>(sizeof(StateNames)/sizeof(StateNames[0]));
	if (state < 0 || state >= StateNameCount)
		return "Unknown";
	return StateNames[state];
}
<commit_msg>Check target AC in power for NaN value<commit_after>#include <cmath>
#include <QDateTime>
#include <QsLog.h>
#include <QTimer>
#include "ac_sensor.h"
#include "control_loop.h"
#include "multi.h"
#include "multi_phase_data.h"
#include "power_info.h"
#include "settings.h"

ControlLoop::ControlLoop(Multi *multi, Phase phase, AcSensor *AcSensor,
						 Settings *settings, Clock *clock, QObject *parent):
	QObject(parent),
	mMulti(multi),
	mAcSensor(AcSensor),
	mSettings(settings),
	mTimer(new QTimer(this)),
	mClock(clock),
	mPhase(phase),
	mMultiUpdate(false),
	mMeterUpdate(false)
{
	Q_ASSERT(multi != 0);
	Q_ASSERT(multi->getPhaseData(phase) != 0);
	Q_ASSERT(AcSensor != 0);
	Q_ASSERT(settings != 0);
	if (mClock == 0)
		mClock.reset(new DefaultClock());
	mTimer->setInterval(5000);
	mTimer->start();
	connect(mMulti, SIGNAL(destroyed()), this, SLOT(onDestroyed()));
	connect(mMulti->getPhaseData(mPhase), SIGNAL(acPowerOutChanged()),
			this, SLOT(onPowerFromMulti()));
	connect(mAcSensor, SIGNAL(destroyed()), this, SLOT(onDestroyed()));
	connect(mAcSensor->getPowerInfo(mPhase), SIGNAL(powerChanged()),
			this, SLOT(onPowerFromMeter()));
	connect(mTimer, SIGNAL(timeout()), this, SLOT(onTimer()));
}

Phase ControlLoop::phase() const
{
	return mPhase;
}

void ControlLoop::onDestroyed()
{
	deleteLater();
}

void ControlLoop::onTimer()
{
	QLOG_DEBUG() << "Update timeout";
	mMultiUpdate = true;
	mMeterUpdate = true;
	checkStep();
}

void ControlLoop::onPowerFromMulti()
{
	mMultiUpdate = true;
	checkStep();
}

void ControlLoop::onPowerFromMeter()
{
	mMeterUpdate = true;
	checkStep();
}

void ControlLoop::checkStep()
{
	if (!mMeterUpdate || !mMultiUpdate)
		return;
	performStep();
	mMeterUpdate = false;
	mMultiUpdate = false;
	mTimer->start();
}

void ControlLoop::performStep()
{
	// pMultiNew < 0: battery is discharging
	// pMultiNew > 0: battery is charging
	const double MaxMultiPower = 32700;
	const double MinMultiPower = -MaxMultiPower;
	double pMultiNew = 0;
	double maxChargePct = qMax(0.0, qMin(100.0, mSettings->maxChargePercentage()));
	double maxDischargePct = qMax(0.0, qMin(100.0, mSettings->maxDischargePercentage()));
	double maxPower = maxChargePct <= 99 ?
			maxChargePct *
			mMulti->maxChargeCurrent() *
			mMulti->dcVoltage() / 100:
			MaxMultiPower;

	if (mSettings->maintenanceInterval() == 0) {
		setHub4State(Hub4External);
	} else if (mSettings->state() == Hub4External) {
		setHub4State(Hub4SelfConsumption);
	}

	switch (mSettings->state()) {
	case Hub4SelfConsumption:
	{
		pMultiNew = computeSetpoint();
		QDateTime nextCharge = mSettings->maintenanceDate();
		if (!nextCharge.isValid()) {
			updateMaintenanceDate();
			nextCharge = mSettings->maintenanceDate();
		}
		nextCharge = nextCharge.addDays(mSettings->maintenanceInterval());
		QDateTime now = mClock->now();
		if (now >= nextCharge && now.time().hour() == 12) {
			setHub4State(Hub4ChargeFromGrid);
		}

		if (isMultiCharged()) {
			setHub4State(Hub4Charged);
		}
		break;
	}
	case Hub4Charged:
		pMultiNew = computeSetpoint();
		if (!isMultiCharged()) {
			setHub4State(Hub4SelfConsumption);
			updateMaintenanceDate();
		}
		break;
	case Hub4External:
		pMultiNew = computeSetpoint();
		break;
	case Hub4ChargeFromGrid:
		pMultiNew = maxPower;
		if (isMultiCharged()) {
			setHub4State(Hub4SelfConsumption);
			updateMaintenanceDate();
		}
		break;
	case Hub4Storage:
		pMultiNew = maxPower;
		if (isMultiCharged())
			mSettings->setMaintenanceDate(QDateTime());
		break;
	}

	bool feedbackDisabled = maxDischargePct < 50;
	// It seems that enabling the the ChargeDisabled flag disables both charge
	// and discharge. So we're only setting the flags when we are not
	// discharging.
	bool chargeDisabled = maxChargePct <= 0 && (
		feedbackDisabled || (
			std::isfinite(pMultiNew) &&
			pMultiNew > 30));
	if (std::isfinite(pMultiNew))
		pMultiNew = qBound(MinMultiPower, pMultiNew, maxPower);

	// Ugly workaround: the value of pMultiNew must always be sent over the
	// D-Bus, even when it does not change, because the multi will reset its
	// power setpoint if no value has been set during the last 10 seconds.
	// So we add a random value to ensure pMultiNew changes. Other solution
	// would involve changes in velib (VBusItem and friends).
	pMultiNew -= qrand() / (10.0 * RAND_MAX);

	mMulti->setIsChargeDisabled(chargeDisabled);
	mMulti->setIsFeedbackDisabled(feedbackDisabled);
	// If feedback is disabled and the multi is 'on', it will start inverting
	// when the grid drops ways (emergency power). If we set the multi to
	// 'charge only', it will not start inverting so the system on AC-In and
	// AC-Out will lose power.
	// mMulti->setMode(feedbackDisabled ? MultiChargerOnly : MultiOn);
	QLOG_TRACE() << mAcSensor->getPowerInfo(mPhase)->power()
				 << '\t' << mMulti->getPhaseData(mPhase)->acPowerIn()
				 << '\t' << pMultiNew
				 << '\t' << chargeDisabled
				 << '\t' << feedbackDisabled;
	if (std::isfinite(pMultiNew))
		mMulti->setAcPowerSetPoint(pMultiNew);
}

double ControlLoop::computeSetpoint() const
{
	PowerInfo *pi = mAcSensor->getPowerInfo(mPhase);
	double pNet = pi->power();
	if (!std::isfinite(pNet))
		return NaN;
	MultiPhaseData *mpd = mMulti->getPhaseData(mPhase);
	double pMulti = mpd->acPowerIn();
	double pTarget = mSettings->acPowerSetPoint();
	// EV: EM340 seems to work better with Alpha = 0.6. Possibly due to lower
	// update rate.
	const double Alpha = 0.8;
	return pMulti + Alpha * (pTarget - pNet);
}

bool ControlLoop::isMultiCharged() const
{
	return mMulti->state() == MultiStateFloat || mMulti->state() == MultiStateStorage;
}

void ControlLoop::updateMaintenanceDate()
{
	QDateTime nextCharge = mClock->now();
	nextCharge.setTime(QTime(1, 0, 0));
	mSettings->setMaintenanceDate(nextCharge);
	QLOG_INFO() << "Next maintenance base date set at:" << nextCharge;
}

void ControlLoop::setHub4State(Hub4State state)
{
	if (mSettings->state() == state)
		return;
	QLOG_INFO() << "Changing Hub4 state from"
				<< getStateName(mSettings->state())
				<< "to" << getStateName(state);
	mSettings->setState(state);
}

const char *ControlLoop::getStateName(int state)
{
	static const char *StateNames[] = { "SelfConsumption", "ChargeFromGrid", "Charged", "Storage", "External" };
	static const int StateNameCount	= static_cast<int>(sizeof(StateNames)/sizeof(StateNames[0]));
	if (state < 0 || state >= StateNameCount)
		return "Unknown";
	return StateNames[state];
}
<|endoftext|>


<commit_before>//===-- SimpleStreamChecker.cpp -----------------------------------------*- C++ -*--//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// Defines a checker for proper use of fopen/fclose APIs.
//   - If a file has been closed with fclose, it should not be accessed again.
//   Accessing a closed file results in undefined behavior.
//   - If a file was opened with fopen, it must be closed with fclose before
//   the execution ends. Failing to do so results in a resource leak.
//
//===----------------------------------------------------------------------===//

#include "ClangSACheckers.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"

using namespace clang;
using namespace ento;

namespace {
typedef llvm::SmallVector<SymbolRef, 2> SymbolVector;

struct StreamState {
private:
  enum Kind { Opened, Closed } K;
  StreamState(Kind InK) : K(InK) { }

public:
  bool isOpened() const { return K == Opened; }
  bool isClosed() const { return K == Closed; }

  static StreamState getOpened() { return StreamState(Opened); }
  static StreamState getClosed() { return StreamState(Closed); }

  bool operator==(const StreamState &X) const {
    return K == X.K;
  }
  void Profile(llvm::FoldingSetNodeID &ID) const {
    ID.AddInteger(K);
  }
};

class SimpleStreamChecker : public Checker<check::PostStmt<CallExpr>,
                                           check::PreStmt<CallExpr>,
                                           check::DeadSymbols > {

  mutable IdentifierInfo *IIfopen, *IIfclose;

  OwningPtr<BugType> DoubleCloseBugType;
  OwningPtr<BugType> LeakBugType;

  void initIdentifierInfo(ASTContext &Ctx) const;

  void reportDoubleClose(SymbolRef FileDescSym,
                         const CallExpr *Call,
                         CheckerContext &C) const;

  void reportLeaks(SymbolVector LeakedStreams,
                   CheckerContext &C,
                   ExplodedNode *ErrNode) const;

public:
  SimpleStreamChecker();

  /// Process fopen.
  void checkPostStmt(const CallExpr *Call, CheckerContext &C) const;
  /// Process fclose.
  void checkPreStmt(const CallExpr *Call, CheckerContext &C) const;

  void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
};

} // end anonymous namespace

/// The state of the checker is a map from tracked stream symbols to their
/// state. Let's store it in the ProgramState.
REGISTER_MAP_WITH_PROGRAMSTATE(StreamMap, SymbolRef, StreamState)

SimpleStreamChecker::SimpleStreamChecker() : IIfopen(0), IIfclose(0) {
  // Initialize the bug types.
  DoubleCloseBugType.reset(new BugType("Double fclose",
                                       "Unix Stream API Error"));

  LeakBugType.reset(new BugType("Resource Leak",
                                "Unix Stream API Error"));
  // Sinks are higher importance bugs as well as calls to assert() or exit(0).
  LeakBugType->setSuppressOnSink(true);
}

void SimpleStreamChecker::checkPostStmt(const CallExpr *Call,
                                        CheckerContext &C) const {
  initIdentifierInfo(C.getASTContext());

  if (C.getCalleeIdentifier(Call) != IIfopen)
    return;

  // Get the symbolic value corresponding to the file handle.
  SymbolRef FileDesc = C.getSVal(Call).getAsSymbol();
  if (!FileDesc)
    return;

  // Generate the next transition (an edge in the exploded graph).
  ProgramStateRef State = C.getState();
  State = State->set<StreamMap>(FileDesc, StreamState::getOpened());
  C.addTransition(State);
}

void SimpleStreamChecker::checkPreStmt(const CallExpr *Call,
                                       CheckerContext &C) const {
  initIdentifierInfo(C.getASTContext());

  if (C.getCalleeIdentifier(Call) != IIfclose || Call->getNumArgs() != 1)
    return;

  // Get the symbolic value corresponding to the file handle.
  SymbolRef FileDesc = C.getSVal(Call->getArg(0)).getAsSymbol();
  if (!FileDesc)
    return;

  // Check if the stream has already been closed.
  ProgramStateRef State = C.getState();
  const StreamState *SS = State->get<StreamMap>(FileDesc);
  if (SS && SS->isClosed()) {
    reportDoubleClose(FileDesc, Call, C);
    return;
  }

  // Generate the next transition, in which the stream is closed.
  State = State->set<StreamMap>(FileDesc, StreamState::getClosed());
  C.addTransition(State);
}

static bool isLeaked(SymbolRef Sym, const StreamState &SS,
                     bool IsSymDead, ProgramStateRef State) {
  if (IsSymDead && SS.isOpened()) {
    // If a symbol is NULL, assume that fopen failed on this path.
    // A symbol should only be considered leaked if it is non-null.
    ConstraintManager &CMgr = State->getConstraintManager();
    ConditionTruthVal OpenFailed = CMgr.isNull(State, Sym);
    return !OpenFailed.isConstrainedTrue();
  }
  return false;
}

void SimpleStreamChecker::checkDeadSymbols(SymbolReaper &SymReaper,
                                           CheckerContext &C) const {
  ProgramStateRef State = C.getState();
  SymbolVector LeakedStreams;
  StreamMapTy TrackedStreams = State->get<StreamMap>();
  for (StreamMapTy::iterator I = TrackedStreams.begin(),
                             E = TrackedStreams.end(); I != E; ++I) {
    SymbolRef Sym = I->first;
    bool IsSymDead = SymReaper.isDead(Sym);

    // Collect leaked symbols.
    if (isLeaked(Sym, I->second, IsSymDead, State))
      LeakedStreams.push_back(Sym);

    // Remove the dead symbol from the streams map.
    if (IsSymDead)
      State = State->remove<StreamMap>(Sym);
  }

  ExplodedNode *N = C.addTransition(State);
  reportLeaks(LeakedStreams, C, N);
}

void SimpleStreamChecker::reportDoubleClose(SymbolRef FileDescSym,
                                            const CallExpr *CallExpr,
                                            CheckerContext &C) const {
  // We reached a bug, stop exploring the path here by generating a sink.
  ExplodedNode *ErrNode = C.generateSink();
  // If we've already reached this node on another path, return.
  if (!ErrNode)
    return;

  // Generate the report.
  BugReport *R = new BugReport(*DoubleCloseBugType,
      "Closing a previously closed file stream", ErrNode);
  R->addRange(CallExpr->getSourceRange());
  R->markInteresting(FileDescSym);
  C.emitReport(R);
}

void SimpleStreamChecker::reportLeaks(SymbolVector LeakedStreams,
                                               CheckerContext &C,
                                               ExplodedNode *ErrNode) const {
  // Attach bug reports to the leak node.
  // TODO: Identify the leaked file descriptor.
  for (llvm::SmallVector<SymbolRef, 2>::iterator
      I = LeakedStreams.begin(), E = LeakedStreams.end(); I != E; ++I) {
    BugReport *R = new BugReport(*LeakBugType,
        "Opened file is never closed; potential resource leak", ErrNode);
    R->markInteresting(*I);
    C.emitReport(R);
  }
}

void SimpleStreamChecker::initIdentifierInfo(ASTContext &Ctx) const {
  if (IIfopen)
    return;
  IIfopen = &Ctx.Idents.get("fopen");
  IIfclose = &Ctx.Idents.get("fclose");
}

void ento::registerSimpleStreamChecker(CheckerManager &mgr) {
  mgr.registerChecker<SimpleStreamChecker>();
}
<commit_msg>[analyzer] Convert SimpleStreamChecker over to CallEvent.<commit_after>//===-- SimpleStreamChecker.cpp -----------------------------------------*- C++ -*--//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// Defines a checker for proper use of fopen/fclose APIs.
//   - If a file has been closed with fclose, it should not be accessed again.
//   Accessing a closed file results in undefined behavior.
//   - If a file was opened with fopen, it must be closed with fclose before
//   the execution ends. Failing to do so results in a resource leak.
//
//===----------------------------------------------------------------------===//

#include "ClangSACheckers.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"

using namespace clang;
using namespace ento;

namespace {
typedef llvm::SmallVector<SymbolRef, 2> SymbolVector;

struct StreamState {
private:
  enum Kind { Opened, Closed } K;
  StreamState(Kind InK) : K(InK) { }

public:
  bool isOpened() const { return K == Opened; }
  bool isClosed() const { return K == Closed; }

  static StreamState getOpened() { return StreamState(Opened); }
  static StreamState getClosed() { return StreamState(Closed); }

  bool operator==(const StreamState &X) const {
    return K == X.K;
  }
  void Profile(llvm::FoldingSetNodeID &ID) const {
    ID.AddInteger(K);
  }
};

class SimpleStreamChecker : public Checker<check::PostCall,
                                           check::PreCall,
                                           check::DeadSymbols > {

  mutable IdentifierInfo *IIfopen, *IIfclose;

  OwningPtr<BugType> DoubleCloseBugType;
  OwningPtr<BugType> LeakBugType;

  void initIdentifierInfo(ASTContext &Ctx) const;

  void reportDoubleClose(SymbolRef FileDescSym,
                         const CallEvent &Call,
                         CheckerContext &C) const;

  void reportLeaks(SymbolVector LeakedStreams,
                   CheckerContext &C,
                   ExplodedNode *ErrNode) const;

public:
  SimpleStreamChecker();

  /// Process fopen.
  void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
  /// Process fclose.
  void checkPreCall(const CallEvent &Call, CheckerContext &C) const;

  void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
};

} // end anonymous namespace

/// The state of the checker is a map from tracked stream symbols to their
/// state. Let's store it in the ProgramState.
REGISTER_MAP_WITH_PROGRAMSTATE(StreamMap, SymbolRef, StreamState)

SimpleStreamChecker::SimpleStreamChecker() : IIfopen(0), IIfclose(0) {
  // Initialize the bug types.
  DoubleCloseBugType.reset(new BugType("Double fclose",
                                       "Unix Stream API Error"));

  LeakBugType.reset(new BugType("Resource Leak",
                                "Unix Stream API Error"));
  // Sinks are higher importance bugs as well as calls to assert() or exit(0).
  LeakBugType->setSuppressOnSink(true);
}

void SimpleStreamChecker::checkPostCall(const CallEvent &Call,
                                        CheckerContext &C) const {
  initIdentifierInfo(C.getASTContext());

  if (!Call.isGlobalCFunction())
    return;

  if (Call.getCalleeIdentifier() != IIfopen)
    return;

  // Get the symbolic value corresponding to the file handle.
  SymbolRef FileDesc = Call.getReturnValue().getAsSymbol();
  if (!FileDesc)
    return;

  // Generate the next transition (an edge in the exploded graph).
  ProgramStateRef State = C.getState();
  State = State->set<StreamMap>(FileDesc, StreamState::getOpened());
  C.addTransition(State);
}

void SimpleStreamChecker::checkPreCall(const CallEvent &Call,
                                       CheckerContext &C) const {
  initIdentifierInfo(C.getASTContext());

  if (!Call.isGlobalCFunction())
    return;

  if (Call.getCalleeIdentifier() != IIfclose)
    return;

  if (Call.getNumArgs() != 1)
    return;

  // Get the symbolic value corresponding to the file handle.
  SymbolRef FileDesc = Call.getArgSVal(0).getAsSymbol();
  if (!FileDesc)
    return;

  // Check if the stream has already been closed.
  ProgramStateRef State = C.getState();
  const StreamState *SS = State->get<StreamMap>(FileDesc);
  if (SS && SS->isClosed()) {
    reportDoubleClose(FileDesc, Call, C);
    return;
  }

  // Generate the next transition, in which the stream is closed.
  State = State->set<StreamMap>(FileDesc, StreamState::getClosed());
  C.addTransition(State);
}

static bool isLeaked(SymbolRef Sym, const StreamState &SS,
                     bool IsSymDead, ProgramStateRef State) {
  if (IsSymDead && SS.isOpened()) {
    // If a symbol is NULL, assume that fopen failed on this path.
    // A symbol should only be considered leaked if it is non-null.
    ConstraintManager &CMgr = State->getConstraintManager();
    ConditionTruthVal OpenFailed = CMgr.isNull(State, Sym);
    return !OpenFailed.isConstrainedTrue();
  }
  return false;
}

void SimpleStreamChecker::checkDeadSymbols(SymbolReaper &SymReaper,
                                           CheckerContext &C) const {
  ProgramStateRef State = C.getState();
  SymbolVector LeakedStreams;
  StreamMapTy TrackedStreams = State->get<StreamMap>();
  for (StreamMapTy::iterator I = TrackedStreams.begin(),
                             E = TrackedStreams.end(); I != E; ++I) {
    SymbolRef Sym = I->first;
    bool IsSymDead = SymReaper.isDead(Sym);

    // Collect leaked symbols.
    if (isLeaked(Sym, I->second, IsSymDead, State))
      LeakedStreams.push_back(Sym);

    // Remove the dead symbol from the streams map.
    if (IsSymDead)
      State = State->remove<StreamMap>(Sym);
  }

  ExplodedNode *N = C.addTransition(State);
  reportLeaks(LeakedStreams, C, N);
}

void SimpleStreamChecker::reportDoubleClose(SymbolRef FileDescSym,
                                            const CallEvent &Call,
                                            CheckerContext &C) const {
  // We reached a bug, stop exploring the path here by generating a sink.
  ExplodedNode *ErrNode = C.generateSink();
  // If we've already reached this node on another path, return.
  if (!ErrNode)
    return;

  // Generate the report.
  BugReport *R = new BugReport(*DoubleCloseBugType,
      "Closing a previously closed file stream", ErrNode);
  R->addRange(Call.getSourceRange());
  R->markInteresting(FileDescSym);
  C.emitReport(R);
}

void SimpleStreamChecker::reportLeaks(SymbolVector LeakedStreams,
                                               CheckerContext &C,
                                               ExplodedNode *ErrNode) const {
  // Attach bug reports to the leak node.
  // TODO: Identify the leaked file descriptor.
  for (llvm::SmallVector<SymbolRef, 2>::iterator
      I = LeakedStreams.begin(), E = LeakedStreams.end(); I != E; ++I) {
    BugReport *R = new BugReport(*LeakBugType,
        "Opened file is never closed; potential resource leak", ErrNode);
    R->markInteresting(*I);
    C.emitReport(R);
  }
}

void SimpleStreamChecker::initIdentifierInfo(ASTContext &Ctx) const {
  if (IIfopen)
    return;
  IIfopen = &Ctx.Idents.get("fopen");
  IIfclose = &Ctx.Idents.get("fclose");
}

void ento::registerSimpleStreamChecker(CheckerManager &mgr) {
  mgr.registerChecker<SimpleStreamChecker>();
}
<|endoftext|>


<commit_before>/*=========================================================================

Program:   Medical Imaging & Interaction Toolkit
Module:    $RCSfile$
Language:  C++
Date:      $Date$
Version:   $Revision$

Copyright (c) German Cancer Research Center, Division of Medical and
Biological Informatics. All rights reserved.
See MITKCopyright.txt or http://www.mitk.org/copyright.html for details.

This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE.  See the above copyright notices for more information.

=========================================================================*/

#include "mitkVerboseLimitedLinearUndo.h"

mitk::VerboseLimitedLinearUndo::VerboseLimitedLinearUndo()
{
}

mitk::VerboseLimitedLinearUndo::~VerboseLimitedLinearUndo()
{
}

bool mitk::VerboseLimitedLinearUndo::SetOperationEvent(UndoStackItem* undoStackItem)
{
  if (!undoStackItem) return false;

  // clear the redolist, if a new operation is saved
  if (!m_RedoList.empty())
  {
    m_RedoList.clear();
    InvokeEvent( RedoEmptyEvent() );
  }

  m_UndoList.push_back(undoStackItem);

  InvokeEvent( UndoNotEmptyEvent() );

  return true;
}

mitk::VerboseLimitedLinearUndo::StackDescription mitk::VerboseLimitedLinearUndo::GetUndoDescriptions()
{
  mitk::VerboseLimitedLinearUndo::StackDescription descriptions;

  if(m_UndoList.empty()) return descriptions;

  int oeid = m_UndoList.back()->GetObjectEventId();  // ObjectEventID of current group
  std::string currentDescription;                    // description of current group
  int currentDescriptionCount(0);                    // counter, how many items of the current group gave descriptions
  bool niceDescriptionFound(false);                  // have we yet seen a plain descriptive entry (not OperationEvent)?

  for ( std::vector<UndoStackItem*>::reverse_iterator iter = m_UndoList.rbegin(); iter != m_UndoList.rend(); ++iter )
  {
    if ( oeid != (*iter)->GetObjectEventId() )
    {
      // current description complete, append to list
      if ( currentDescription.empty() )
        currentDescription = "Some unnamed action";  // set a default description

      descriptions.push_back( StackDescriptionItem(oeid,currentDescription) );

      currentDescription = "";                 // prepare for next group
      currentDescriptionCount = 0;
      niceDescriptionFound = false;
      oeid = (*iter)->GetObjectEventId();
    }

    if ( !(*iter)->GetDescription().empty() )  // if there is a description
    {
      if ( currentDescriptionCount )           // if we have already seen another description
//        currentDescription += '\n';            // concatenate descriptions with newline
        currentDescription += " AND ";          // this has to wait until the popup can process multiline items

      if (!dynamic_cast<OperationEvent*>(*iter))
      {
        // anything but an OperationEvent overrides the collected descriptions
        currentDescription = (*iter)->GetDescription();
        niceDescriptionFound = true;
      }
      else if (!niceDescriptionFound) // mere descriptive items override OperationEvents' descriptions
      {
        currentDescription += (*iter)->GetDescription();
      }

      ++currentDescriptionCount;
    }

  } // for

  // add last description to list
  if ( currentDescription.empty() )
    currentDescription = "Some unnamed action";
  descriptions.push_back( StackDescriptionItem( oeid, currentDescription) );

  return descriptions;  // list ready
}

mitk::VerboseLimitedLinearUndo::StackDescription mitk::VerboseLimitedLinearUndo::GetRedoDescriptions()
{
  mitk::VerboseLimitedLinearUndo::StackDescription descriptions;

  if(m_RedoList.empty()) return descriptions;

  int oeid = m_RedoList.back()->GetObjectEventId();  // ObjectEventID of current group
  std::string currentDescription;                    // description of current group
  int currentDescriptionCount(0);                    // counter, how many items of the current group gave descriptions
  bool niceDescriptionFound(false);                  // have we yet seen a plain descriptive entry (not OperationEvent)?

  for ( std::vector<UndoStackItem*>::reverse_iterator iter = m_RedoList.rbegin(); iter != m_RedoList.rend(); ++iter )
  {
    if ( oeid != (*iter)->GetObjectEventId() )
    {
      // current description complete, append to list
      if ( currentDescription.empty() )
        currentDescription = "Some unnamed action";  // set a default description

      descriptions.push_back( StackDescriptionItem( oeid, currentDescription) );

      currentDescription = "";                 // prepare for next group
      currentDescriptionCount = 0;
      niceDescriptionFound = false;
      oeid = (*iter)->GetObjectEventId();
    }

    if ( !(*iter)->GetDescription().empty() )  // if there is a description
    {
      if ( currentDescriptionCount )           // if we have already seen another description
//        currentDescription += '\n';            // concatenate descriptions with newline
        currentDescription += " AND ";          // this has to wait until the popup can process multiline items

      if (!dynamic_cast<OperationEvent*>(*iter))
      {
        // anything but an OperationEvent overrides the collected descriptions
        currentDescription = (*iter)->GetDescription();
        niceDescriptionFound = true;
      }
      else if (!niceDescriptionFound) // mere descriptive items override OperationEvents' descriptions
      {
        currentDescription += (*iter)->GetDescription();
      }

      ++currentDescriptionCount;
    }

  } // for

  // add last description to list
  if ( currentDescription.empty() )
    currentDescription = "Some unnamed action";
  descriptions.push_back( StackDescriptionItem( oeid, currentDescription) );

  return descriptions;  // list ready
}

<commit_msg>String concatenation changed<commit_after>/*=========================================================================

Program:   Medical Imaging & Interaction Toolkit
Module:    $RCSfile$
Language:  C++
Date:      $Date$
Version:   $Revision$

Copyright (c) German Cancer Research Center, Division of Medical and
Biological Informatics. All rights reserved.
See MITKCopyright.txt or http://www.mitk.org/copyright.html for details.

This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE.  See the above copyright notices for more information.

=========================================================================*/

#include "mitkVerboseLimitedLinearUndo.h"

mitk::VerboseLimitedLinearUndo::VerboseLimitedLinearUndo()
{
}

mitk::VerboseLimitedLinearUndo::~VerboseLimitedLinearUndo()
{
}

bool mitk::VerboseLimitedLinearUndo::SetOperationEvent(UndoStackItem* undoStackItem)
{
  if (!undoStackItem) return false;

  // clear the redolist, if a new operation is saved
  if (!m_RedoList.empty())
  {
    m_RedoList.clear();
    InvokeEvent( RedoEmptyEvent() );
  }

  m_UndoList.push_back(undoStackItem);

  InvokeEvent( UndoNotEmptyEvent() );

  return true;
}

mitk::VerboseLimitedLinearUndo::StackDescription mitk::VerboseLimitedLinearUndo::GetUndoDescriptions()
{
  mitk::VerboseLimitedLinearUndo::StackDescription descriptions;

  if(m_UndoList.empty()) return descriptions;

  int oeid = m_UndoList.back()->GetObjectEventId();  // ObjectEventID of current group
  std::string currentDescription;                    // description of current group
  int currentDescriptionCount(0);                    // counter, how many items of the current group gave descriptions
  bool niceDescriptionFound(false);                  // have we yet seen a plain descriptive entry (not OperationEvent)?

  for ( std::vector<UndoStackItem*>::reverse_iterator iter = m_UndoList.rbegin(); iter != m_UndoList.rend(); ++iter )
  {
    if ( oeid != (*iter)->GetObjectEventId() )
    {
      // current description complete, append to list
      if ( currentDescription.empty() )
        currentDescription = "Some unnamed action";  // set a default description

      descriptions.push_back( StackDescriptionItem(oeid,currentDescription) );

      currentDescription = "";                 // prepare for next group
      currentDescriptionCount = 0;
      niceDescriptionFound = false;
      oeid = (*iter)->GetObjectEventId();
    }

    if ( !(*iter)->GetDescription().empty() )  // if there is a description
    {
      if (!dynamic_cast<OperationEvent*>(*iter))
      {
        // anything but an OperationEvent overrides the collected descriptions
        currentDescription = (*iter)->GetDescription();
        niceDescriptionFound = true;
      }
      else if (!niceDescriptionFound) // mere descriptive items override OperationEvents' descriptions
      {
      if ( currentDescriptionCount )            // if we have already seen another description
        //currentDescription += '\n';             // concatenate descriptions with newline
        currentDescription += " AND ";          // this has to wait until the popup can process multiline items
        currentDescription += (*iter)->GetDescription();
      }

      ++currentDescriptionCount;
    }

  } // for

  // add last description to list
  if ( currentDescription.empty() )
    currentDescription = "Some unnamed action";
  descriptions.push_back( StackDescriptionItem( oeid, currentDescription) );

  return descriptions;  // list ready
}

mitk::VerboseLimitedLinearUndo::StackDescription mitk::VerboseLimitedLinearUndo::GetRedoDescriptions()
{
  mitk::VerboseLimitedLinearUndo::StackDescription descriptions;

  if(m_RedoList.empty()) return descriptions;

  int oeid = m_RedoList.back()->GetObjectEventId();  // ObjectEventID of current group
  std::string currentDescription;                    // description of current group
  int currentDescriptionCount(0);                    // counter, how many items of the current group gave descriptions
  bool niceDescriptionFound(false);                  // have we yet seen a plain descriptive entry (not OperationEvent)?

  for ( std::vector<UndoStackItem*>::reverse_iterator iter = m_RedoList.rbegin(); iter != m_RedoList.rend(); ++iter )
  {
    if ( oeid != (*iter)->GetObjectEventId() )
    {
      // current description complete, append to list
      if ( currentDescription.empty() )
        currentDescription = "Some unnamed action";  // set a default description

      descriptions.push_back( StackDescriptionItem( oeid, currentDescription) );

      currentDescription = "";                 // prepare for next group
      currentDescriptionCount = 0;
      niceDescriptionFound = false;
      oeid = (*iter)->GetObjectEventId();
    }

    if ( !(*iter)->GetDescription().empty() )  // if there is a description
    {

      if (!dynamic_cast<OperationEvent*>(*iter))
      {
        // anything but an OperationEvent overrides the collected descriptions
        currentDescription = (*iter)->GetDescription();
        niceDescriptionFound = true;
      }
      else if (!niceDescriptionFound) // mere descriptive items override OperationEvents' descriptions
      {
      if ( currentDescriptionCount )            // if we have already seen another description
        //currentDescription += '\n';             // concatenate descriptions with newline
        currentDescription += " AND ";          // this has to wait until the popup can process multiline items
        currentDescription += (*iter)->GetDescription();
      }

      ++currentDescriptionCount;
    }

  } // for

  // add last description to list
  if ( currentDescription.empty() )
    currentDescription = "Some unnamed action";
  descriptions.push_back( StackDescriptionItem( oeid, currentDescription) );

  return descriptions;  // list ready
}

<|endoftext|>