<commit_before>// Copyright(c) 2016 Jounayd Id Salah
// Distributed under the MIT License (See accompanying file LICENSE.md file or copy at http://opensource.org/licenses/MIT).
#include "prebuild/pch.h"
#include "prebuild/generator/plugins/coCppTypesGeneratorPlugin.h"
#include "prebuild/generator/plugins/coCppGeneratorUtils.h"
#include "prebuild/generator/coProjectGenerator.h"
#include "lang/result/coResult_f.h"
#include "lang/reflect/coType.h"
#include "lang/reflect/coField.h"
#include "io/path/coPath_f.h"
#include "io/file/coFileStreamBuffer.h"
#include "io/dir/coDirectory_f.h"
#include "io/stream/coStringOutputStream.h"
#include "parser/project/coParsedProject.h"
#include "parser/source/coParsedType.h"
#include "parser/source/coParsedField.h"
#include "memory/allocator/coLocalAllocator.h"

static const coConstString co_genPath = "gen";
static const coConstString co_typesPath = "types";

coResult coCppTypesGeneratorPlugin::OnInit(const coObject::InitConfig& _config)
{
	coTRY(Super::OnInit(_config), nullptr);

	return true;
}

coResult coCppTypesGeneratorPlugin::Generate(const coParsedProject& _parsedProject)
{
	coTRY(Super::Generate(_parsedProject), nullptr);

	const coConstString& outDir = projectGenerator->GetOutReferenceDir();
	const coConstString& projectPath = projectGenerator->GetProjectRelativePath();
	coJoinPaths(projectGenDir, outDir, projectPath);
	coASSERT(coIsPathNormalized(projectGenDir));
	coTRY(coCreateDirsIfMissing(projectGenDir), "Failed to create: " << projectGenDir);

	coJoinPaths(typesGenDir, projectGenDir, co_typesPath);
	coASSERT(coIsPathNormalized(typesGenDir));
	coTRY(coCreateDirsIfMissing(typesGenDir), "Failed to create: " << typesGenDir);

	coTRY(GenerateTypes(_parsedProject), "Failed to generate types.");

	return true;
}

coResult coCppTypesGeneratorPlugin::GenerateTypes(const coParsedProject& _parsedProject)
{
	coLocalAllocator localAllocator(4048);

	// Paths
	coDynamicString mainCxxAbsolutePath(localAllocator);
	coDynamicString mainCxxRelativePath(localAllocator);
	{
		coJoinPaths(mainCxxRelativePath, projectGenerator->GetProjectRelativePath(), "allTypes.cxx");
		coASSERT(coIsPathNormalized(mainCxxRelativePath));
		coJoinPaths(mainCxxAbsolutePath, projectGenerator->GetOutReferenceDir(), mainCxxRelativePath);
		coASSERT(coIsPathNormalized(mainCxxAbsolutePath));
	}

	coFileStreamBuffer streamBuffer;
	{
		coFileStreamBuffer::InitConfig c;
		c.path = mainCxxAbsolutePath;
		c.mode = coFileStreamBuffer::write;
		coTRY(streamBuffer.Init(c), "Failed to open for writing: " << streamBuffer.GetDebugName());
	}

	coStringOutputStream stream;
	{
		coStringOutputStream::InitConfig c;
		c.buffer = &streamBuffer;
		coTRY(stream.Init(c), nullptr);
	}

	coWriteHeader(stream);
	stream << "\n";

	coDynamicString cxxPath(localAllocator);

	for (const coParsedType* parsedType : _parsedProject.parsedTypes)
	{
		coASSERT(parsedType);
		coTRY(GenerateType(cxxPath, *parsedType), "Failed to generate type: " << parsedType->GetDebugName());
		coWriteInclude(stream, cxxPath);
	}

	stream.Flush();
	coTRY(stream.GetResult(), "Failed to write to stream: " << stream.GetDebugName());

	projectGenerator->AddGeneratedEntryPath(mainCxxRelativePath);

	return true;
}

coResult coCppTypesGeneratorPlugin::GenerateType(coDynamicString& _relativePath, const coParsedType& _parsedType)
{
	const coType* type = _parsedType.type;
	coASSERT(type);
	coTRY(type->name != "", "Empty type name");

	coLocalAllocator localAllocator(4048);
	coDynamicString absolutePath(localAllocator);

	// Paths
	{
		const coConstString& projectPath = projectGenerator->GetProjectRelativePath();
		coDynamicString tmpPath(localAllocator);
		coJoinPaths(tmpPath, projectPath, co_typesPath);
		coJoinPaths(_relativePath, tmpPath, type->name);
		_relativePath << ".cxx";

		const coConstString& outPath = projectGenerator->GetOutReferenceDir();
		coJoinPaths(absolutePath, outPath, _relativePath);
		coASSERT(coIsPathNormalized(_relativePath));
	}

	coFileStreamBuffer streamBuffer;
	{
		coFileStreamBuffer::InitConfig c;
		c.path = absolutePath;
		c.mode = coFileStreamBuffer::write;
		coTRY(streamBuffer.Init(c), "Failed to open for writing: " << streamBuffer.GetDebugName());
	}

	coStringOutputStream stream;
	{
		coStringOutputStream::InitConfig c;
		c.buffer = &streamBuffer;
		coTRY(stream.Init(c), nullptr);
	}

	coWriteHeader(stream);
	coWriteInclude(stream, _parsedType.sourcePath);
	coWriteInclude(stream, "lang/reflect/coType.h");
	coWriteInclude(stream, "lang/reflect/coType_f.h");
	coWriteInclude(stream, "lang/result/coResult_f.h");
	coWriteInclude(stream, "lang/reflect/coTypeBuilder.h");
	coWriteInclude(stream, "lang/reflect/coTypeAutoRegistrator.h");
	if (_parsedType.parsedFields.count)
	{
		coWriteInclude(stream, "lang/reflect/coField.h");
		coWriteInclude(stream, "lang/reflect/coField_f.h");
	}
	coWriteInclude(stream, "lang/result/coResult.h");
	coWriteInclude(stream, "container/string/coDynamicString_f.h");
	stream << "\n";
	coTRY(WriteTypeBuilderDeclaration(stream, _parsedType), nullptr);
	coTRY(WriteInitTypeFunc(stream, _parsedType), nullptr);
	coTRY(WriteLinkTypeFunc(stream, _parsedType), nullptr);
	coTRY(WriteGetStaticTypeFunc(stream, _parsedType), nullptr);

	stream.Flush();
	coTRY(stream.GetResult(), "Failed to write to stream: "<<stream);

	return true;
}

coResult coCppTypesGeneratorPlugin::WriteInitTypeFunc(coStringOutputStream& _stream, const coParsedType& _parsedType)
{
	const coType* type = _parsedType.type;
	coASSERT(type);
	coASSERT(type->name != nullptr);
	_stream << "coResult " << type->name << "_typeBuilder::OnInitType()\n";
	_stream << "{\n";
	coTRY(WriteParsedType(_stream, _parsedType, "\t"), "Failed to write type: " << _parsedType.GetDebugName());
	_stream << "\treturn true;\n";
	_stream << "}\n";
	_stream << "\n";
	return true;
}

coResult coCppTypesGeneratorPlugin::WriteLinkTypeFunc(coStringOutputStream& _stream, const coParsedType& _parsedType)
{
	const coType* type = _parsedType.type;
	coASSERT(type);
	coASSERT(type->name != nullptr);
	_stream << "coResult " << type->name << "_typeBuilder::OnLinkType()\n";
	_stream << "{\n";
	if (_parsedType.superTypeName != "")
	{
		_stream << "\tcoTRY(type, nullptr);\n";
		_stream << "\ttype->super = coGetType<" << _parsedType.superTypeName << ">();\n";
		_stream << "\n";
	}
	_stream << "\treturn true;\n";
	_stream << "}\n";
	_stream << "\n";
	return true;
}

coResult coCppTypesGeneratorPlugin::WriteTypeBuilderDeclaration(coStringOutputStream& _stream, const coParsedType& _parsedType)
{
	const coType* type = _parsedType.type;
	coASSERT(type);

	_stream << "class " << type->name << "_typeBuilder : public coTypeBuilder\n";
	_stream << "{\n";
	_stream << "public:\n";
	_stream << "\tvirtual coResult OnInitType() override;\n";
	_stream << "\tvirtual coResult OnLinkType() override;\n";
	_stream << "};\n";
	_stream << type->name << "_typeBuilder co_typeBuilder_" << type->name << ";\n";
	_stream << "coTypeAutoRegistrator co_typeAutoRegistrator_" << type->name << "(co_typeBuilder_" << type->name << ");\n";
	_stream << "\n";
	return true;
}

coResult coCppTypesGeneratorPlugin::WriteGetStaticTypeFunc(coStringOutputStream& _stream, const coParsedType& _parsedType)
{
	const coType* type = _parsedType.type;
	coASSERT(type);

	_stream << "const coType* " << type->name << "::GetStaticType()\n";
	_stream << "{\n";
	_stream << "\treturn co_typeBuilder_" << type->name << ".GetType();\n";
	_stream << "}\n";
	_stream << "\n";
	return true;
}

coResult coCppTypesGeneratorPlugin::WriteSymbol(coStringOutputStream& _stream, const coSymbol& _symbol, const coConstString& _indentation, const coConstString& _prefix)
{
	_stream << _indentation << _prefix << "name = \"" << _symbol.name << "\";\n";
	_stream << _indentation << _prefix << "nameHash = coHash32(" << _prefix << "name);\n";
	_stream << _indentation << _prefix << "symbolFlags = " << _symbol.symbolFlags << ";\n";
	return true;
}

coResult coCppTypesGeneratorPlugin::WriteParsedType(coStringOutputStream& _stream, const coParsedType& _parsedType, const coConstString& _indentation)
{
	const coType* type = _parsedType.type;
	coASSERT(type);

	coTRY(WriteSymbol(_stream, *type, "\t", "type->"), nullptr);
	_stream << _indentation << "type->size8 = sizeof(" << type->name << ");\n";
	_stream << _indentation << "type->alignment8 = alignof(" << type->name << ");\n";
	_stream << _indentation << "type->super = nullptr;\n";
	_stream << _indentation << "type->createFunc = []() -> void* { return new " << type->name << "(); };\n";

	_stream << _indentation << "\n";

	// Fields
	{
		coUint nbPublicFields = 0;
		for (const coParsedField* parsedField : _parsedType.parsedFields)
		{
			coASSERT(parsedField);
			const coField* field = parsedField->field;
			if (field->symbolFlags & coSymbol::public_)
			{
				++nbPublicFields;
			}
		}

		if (nbPublicFields)
		{
			_stream << _indentation << "// Fields\n";
			_stream << _indentation << "coReserve(type->fields, " << nbPublicFields << ");\n";
			_stream << _indentation << "\n";
			for (const coParsedField* parsedField : _parsedType.parsedFields)
			{
				coASSERT(parsedField);
				const coField* field = parsedField->field;
				coASSERT(field);
				if (field->symbolFlags & coSymbol::public_)
				{
					coTRY(WriteParsedField(_stream, _parsedType, *parsedField, _indentation), "Failed to write field: " << parsedField->field->name);
				}
			}
		}
	}

	return true;
}

coResult coCppTypesGeneratorPlugin::WriteParsedField(coStringOutputStream& _stream, const coParsedType& _parsedType, const coParsedField& _parsedField, const coConstString& _indentation)
{
	coLocalAllocator localAllocator(2048);

	const coField* field = _parsedField.field;
	coASSERT(field);
	_stream << _indentation << "// " << field->name << "\n";
	_stream << _indentation << "{\n";

	coDynamicString blockIndent(localAllocator);
	blockIndent << _indentation << "\t";
	_stream << blockIndent << "coField* field = new coField();\n";
	coTRY(WriteSymbol(_stream, *field, blockIndent, "field->"), nullptr);
	_stream << blockIndent << "field->type = nullptr;\n";
	_stream << blockIndent << "field->offset8 = static_cast<decltype(field->offset8)>(coGetFieldOffset<" << _parsedType.type->name << ">(&" << _parsedType.type->name << "::" << field->name << "));\n";

	_stream << blockIndent << "\n";
	_stream << blockIndent << "coPushBack(type->fields, field);\n";
	_stream << _indentation << "}\n";
	_stream << _indentation << "\n";
	coASSERT(field);
	return true;
}

<commit_msg>Fixed cpp reflection generator not initializing field types.<commit_after>// Copyright(c) 2016 Jounayd Id Salah
// Distributed under the MIT License (See accompanying file LICENSE.md file or copy at http://opensource.org/licenses/MIT).
#include "prebuild/pch.h"
#include "prebuild/generator/plugins/coCppTypesGeneratorPlugin.h"
#include "prebuild/generator/plugins/coCppGeneratorUtils.h"
#include "prebuild/generator/coProjectGenerator.h"
#include "lang/result/coResult_f.h"
#include "lang/reflect/coType.h"
#include "lang/reflect/coField.h"
#include "io/path/coPath_f.h"
#include "io/file/coFileStreamBuffer.h"
#include "io/dir/coDirectory_f.h"
#include "io/stream/coStringOutputStream.h"
#include "parser/project/coParsedProject.h"
#include "parser/source/coParsedType.h"
#include "parser/source/coParsedField.h"
#include "memory/allocator/coLocalAllocator.h"

static const coConstString co_genPath = "gen";
static const coConstString co_typesPath = "types";

coResult coCppTypesGeneratorPlugin::OnInit(const coObject::InitConfig& _config)
{
	coTRY(Super::OnInit(_config), nullptr);

	return true;
}

coResult coCppTypesGeneratorPlugin::Generate(const coParsedProject& _parsedProject)
{
	coTRY(Super::Generate(_parsedProject), nullptr);

	const coConstString& outDir = projectGenerator->GetOutReferenceDir();
	const coConstString& projectPath = projectGenerator->GetProjectRelativePath();
	coJoinPaths(projectGenDir, outDir, projectPath);
	coASSERT(coIsPathNormalized(projectGenDir));
	coTRY(coCreateDirsIfMissing(projectGenDir), "Failed to create: " << projectGenDir);

	coJoinPaths(typesGenDir, projectGenDir, co_typesPath);
	coASSERT(coIsPathNormalized(typesGenDir));
	coTRY(coCreateDirsIfMissing(typesGenDir), "Failed to create: " << typesGenDir);

	coTRY(GenerateTypes(_parsedProject), "Failed to generate types.");

	return true;
}

coResult coCppTypesGeneratorPlugin::GenerateTypes(const coParsedProject& _parsedProject)
{
	coLocalAllocator localAllocator(4048);

	// Paths
	coDynamicString mainCxxAbsolutePath(localAllocator);
	coDynamicString mainCxxRelativePath(localAllocator);
	{
		coJoinPaths(mainCxxRelativePath, projectGenerator->GetProjectRelativePath(), "allTypes.cxx");
		coASSERT(coIsPathNormalized(mainCxxRelativePath));
		coJoinPaths(mainCxxAbsolutePath, projectGenerator->GetOutReferenceDir(), mainCxxRelativePath);
		coASSERT(coIsPathNormalized(mainCxxAbsolutePath));
	}

	coFileStreamBuffer streamBuffer;
	{
		coFileStreamBuffer::InitConfig c;
		c.path = mainCxxAbsolutePath;
		c.mode = coFileStreamBuffer::write;
		coTRY(streamBuffer.Init(c), "Failed to open for writing: " << streamBuffer.GetDebugName());
	}

	coStringOutputStream stream;
	{
		coStringOutputStream::InitConfig c;
		c.buffer = &streamBuffer;
		coTRY(stream.Init(c), nullptr);
	}

	coWriteHeader(stream);
	stream << "\n";

	coDynamicString cxxPath(localAllocator);

	for (const coParsedType* parsedType : _parsedProject.parsedTypes)
	{
		coASSERT(parsedType);
		coTRY(GenerateType(cxxPath, *parsedType), "Failed to generate type: " << parsedType->GetDebugName());
		coWriteInclude(stream, cxxPath);
	}

	stream.Flush();
	coTRY(stream.GetResult(), "Failed to write to stream: " << stream.GetDebugName());

	projectGenerator->AddGeneratedEntryPath(mainCxxRelativePath);

	return true;
}

coResult coCppTypesGeneratorPlugin::GenerateType(coDynamicString& _relativePath, const coParsedType& _parsedType)
{
	const coType* type = _parsedType.type;
	coASSERT(type);
	coTRY(type->name != "", "Empty type name");

	coLocalAllocator localAllocator(4048);
	coDynamicString absolutePath(localAllocator);

	// Paths
	{
		const coConstString& projectPath = projectGenerator->GetProjectRelativePath();
		coDynamicString tmpPath(localAllocator);
		coJoinPaths(tmpPath, projectPath, co_typesPath);
		coJoinPaths(_relativePath, tmpPath, type->name);
		_relativePath << ".cxx";

		const coConstString& outPath = projectGenerator->GetOutReferenceDir();
		coJoinPaths(absolutePath, outPath, _relativePath);
		coASSERT(coIsPathNormalized(_relativePath));
	}

	coFileStreamBuffer streamBuffer;
	{
		coFileStreamBuffer::InitConfig c;
		c.path = absolutePath;
		c.mode = coFileStreamBuffer::write;
		coTRY(streamBuffer.Init(c), "Failed to open for writing: " << streamBuffer.GetDebugName());
	}

	coStringOutputStream stream;
	{
		coStringOutputStream::InitConfig c;
		c.buffer = &streamBuffer;
		coTRY(stream.Init(c), nullptr);
	}

	coWriteHeader(stream);
	coWriteInclude(stream, _parsedType.sourcePath);
	coWriteInclude(stream, "lang/reflect/coType.h");
	coWriteInclude(stream, "lang/reflect/coType_f.h");
	coWriteInclude(stream, "lang/result/coResult_f.h");
	coWriteInclude(stream, "lang/reflect/coTypeBuilder.h");
	coWriteInclude(stream, "lang/reflect/coTypeAutoRegistrator.h");
	if (_parsedType.parsedFields.count)
	{
		coWriteInclude(stream, "lang/reflect/coField.h");
		coWriteInclude(stream, "lang/reflect/coField_f.h");
	}
	coWriteInclude(stream, "lang/result/coResult.h");
	coWriteInclude(stream, "container/string/coDynamicString_f.h");
	stream << "\n";
	coTRY(WriteTypeBuilderDeclaration(stream, _parsedType), nullptr);
	coTRY(WriteInitTypeFunc(stream, _parsedType), nullptr);
	coTRY(WriteLinkTypeFunc(stream, _parsedType), nullptr);
	coTRY(WriteGetStaticTypeFunc(stream, _parsedType), nullptr);

	stream.Flush();
	coTRY(stream.GetResult(), "Failed to write to stream: "<<stream);

	return true;
}

coResult coCppTypesGeneratorPlugin::WriteInitTypeFunc(coStringOutputStream& _stream, const coParsedType& _parsedType)
{
	const coType* type = _parsedType.type;
	coASSERT(type);
	coASSERT(type->name != nullptr);
	_stream << "coResult " << type->name << "_typeBuilder::OnInitType()\n";
	_stream << "{\n";
	coTRY(WriteParsedType(_stream, _parsedType, "\t"), "Failed to write type: " << _parsedType.GetDebugName());
	_stream << "\treturn true;\n";
	_stream << "}\n";
	_stream << "\n";
	return true;
}

coResult coCppTypesGeneratorPlugin::WriteLinkTypeFunc(coStringOutputStream& _stream, const coParsedType& _parsedType)
{
	const coType* type = _parsedType.type;
	coASSERT(type);
	coASSERT(type->name != nullptr);
	_stream << "coResult " << type->name << "_typeBuilder::OnLinkType()\n";
	_stream << "{\n";
	if (_parsedType.superTypeName != "")
	{
		_stream << "\tcoTRY(type, nullptr);\n";
		_stream << "\ttype->super = coGetType<" << _parsedType.superTypeName << ">();\n";
		_stream << "\n";
	}
	coUint32 fieldIndex = 0;
	for (const coParsedField* parsedField : _parsedType.parsedFields)
	{
		const coField* field = parsedField->field;
		coASSERT(field);
		if (field->symbolFlags & coSymbol::public_)
		{
			coTRY(parsedField->typeName.count, nullptr);
			_stream << "\ttype->fields["<< fieldIndex<<"]->type = coGetType<" << parsedField->typeName << ">();\n";
			++fieldIndex;
		}
	}
	_stream << "\treturn true;\n";
	_stream << "}\n";
	_stream << "\n";
	return true;
}

coResult coCppTypesGeneratorPlugin::WriteTypeBuilderDeclaration(coStringOutputStream& _stream, const coParsedType& _parsedType)
{
	const coType* type = _parsedType.type;
	coASSERT(type);

	_stream << "class " << type->name << "_typeBuilder : public coTypeBuilder\n";
	_stream << "{\n";
	_stream << "public:\n";
	_stream << "\tvirtual coResult OnInitType() override;\n";
	_stream << "\tvirtual coResult OnLinkType() override;\n";
	_stream << "};\n";
	_stream << type->name << "_typeBuilder co_typeBuilder_" << type->name << ";\n";
	_stream << "coTypeAutoRegistrator co_typeAutoRegistrator_" << type->name << "(co_typeBuilder_" << type->name << ");\n";
	_stream << "\n";
	return true;
}

coResult coCppTypesGeneratorPlugin::WriteGetStaticTypeFunc(coStringOutputStream& _stream, const coParsedType& _parsedType)
{
	const coType* type = _parsedType.type;
	coASSERT(type);

	_stream << "const coType* " << type->name << "::GetStaticType()\n";
	_stream << "{\n";
	_stream << "\treturn co_typeBuilder_" << type->name << ".GetType();\n";
	_stream << "}\n";
	_stream << "\n";
	return true;
}

coResult coCppTypesGeneratorPlugin::WriteSymbol(coStringOutputStream& _stream, const coSymbol& _symbol, const coConstString& _indentation, const coConstString& _prefix)
{
	_stream << _indentation << _prefix << "name = \"" << _symbol.name << "\";\n";
	_stream << _indentation << _prefix << "nameHash = coHash32(" << _prefix << "name);\n";
	_stream << _indentation << _prefix << "symbolFlags = " << _symbol.symbolFlags << ";\n";
	return true;
}

coResult coCppTypesGeneratorPlugin::WriteParsedType(coStringOutputStream& _stream, const coParsedType& _parsedType, const coConstString& _indentation)
{
	const coType* type = _parsedType.type;
	coASSERT(type);

	coTRY(WriteSymbol(_stream, *type, "\t", "type->"), nullptr);
	_stream << _indentation << "type->size8 = sizeof(" << type->name << ");\n";
	_stream << _indentation << "type->alignment8 = alignof(" << type->name << ");\n";
	_stream << _indentation << "type->super = nullptr;\n";
	_stream << _indentation << "type->createFunc = []() -> void* { return new " << type->name << "(); };\n";

	_stream << _indentation << "\n";

	// Fields
	{
		coUint nbPublicFields = 0;
		for (const coParsedField* parsedField : _parsedType.parsedFields)
		{
			coASSERT(parsedField);
			const coField* field = parsedField->field;
			if (field->symbolFlags & coSymbol::public_)
			{
				++nbPublicFields;
			}
		}

		if (nbPublicFields)
		{
			_stream << _indentation << "// Fields\n";
			_stream << _indentation << "coReserve(type->fields, " << nbPublicFields << ");\n";
			_stream << _indentation << "\n";
			for (const coParsedField* parsedField : _parsedType.parsedFields)
			{
				coASSERT(parsedField);
				const coField* field = parsedField->field;
				coASSERT(field);
				if (field->symbolFlags & coSymbol::public_)
				{
					coTRY(WriteParsedField(_stream, _parsedType, *parsedField, _indentation), "Failed to write field: " << parsedField->field->name);
				}
			}
		}
	}

	return true;
}

coResult coCppTypesGeneratorPlugin::WriteParsedField(coStringOutputStream& _stream, const coParsedType& _parsedType, const coParsedField& _parsedField, const coConstString& _indentation)
{
	coLocalAllocator localAllocator(2048);

	const coField* field = _parsedField.field;
	coASSERT(field);
	_stream << _indentation << "// " << field->name << "\n";
	_stream << _indentation << "{\n";

	coDynamicString blockIndent(localAllocator);
	blockIndent << _indentation << "\t";
	_stream << blockIndent << "coField* field = new coField();\n";
	coTRY(WriteSymbol(_stream, *field, blockIndent, "field->"), nullptr);
	_stream << blockIndent << "field->type = nullptr;\n";
	_stream << blockIndent << "field->offset8 = static_cast<decltype(field->offset8)>(coGetFieldOffset<" << _parsedType.type->name << ">(&" << _parsedType.type->name << "::" << field->name << "));\n";

	_stream << blockIndent << "\n";
	_stream << blockIndent << "coPushBack(type->fields, field);\n";
	_stream << _indentation << "}\n";
	_stream << _indentation << "\n";
	coASSERT(field);
	return true;
}

<|endoftext|>


<commit_before>// This file is part of the "x0" project, http://github.com/christianparpart/x0>
//   (c) 2009-2017 Christian Parpart <christian@parpart.family>
//
// Licensed under the MIT License (the "License"); you may not use this
// file except in compliance with the License. You may obtain a copy of
// the License at: http://opensource.org/licenses/MIT

#include "dirlisting.h"
#include <xzero/http/HttpRequest.h>
#include <xzero/http/HttpResponse.h>
#include <xzero/io/FileUtil.h>
#include <xzero/io/File.h>
#include <xzero/logging.h>

using namespace xzero;
using namespace xzero::http;
using namespace xzero::flow;

namespace x0d {

DirlistingModule::DirlistingModule(x0d::XzeroDaemon* d)
    : XzeroModule(d, "dirlisting") {

  mainHandler("dirlisting", &DirlistingModule::dirlisting);
}

bool DirlistingModule::dirlisting(XzeroContext* cx, Params& args) {
  if (!cx->verifyDirectoryDepth())
    return true;

  if (!cx->file()) {
    logError("dirlisting", "Requesth path not mapped to a physical location yet.");
    return false;
  }

  if (!cx->file()->isDirectory())
    return false;

  Buffer sstr;

  appendHeader(sstr, cx->request()->path());

  if (cx->request()->path() != "/")
    appendDirectory(sstr, "..");

  FileUtil::ls(cx->file()->path(), [&](const std::string& path) -> bool {
    std::shared_ptr<File> file = daemon().vfs().getFile(path);
    if (file->isDirectory()) {
      appendDirectory(sstr, file->filename());
    } else {
      appendFile(sstr, file);
    }
    return true;
  });
  appendTrailer(sstr);

  cx->response()->setStatus(HttpStatus::Ok);
  cx->response()->setContentLength(sstr.size());
  cx->response()->setHeader("Content-Type", "text/html");
  cx->response()->write(std::move(sstr));
  cx->response()->completed();

  return true;
}

void DirlistingModule::appendHeader(Buffer& sstr, const std::string& path) {
  sstr << ""
       << "<title>Directory: " << path << "</title>"
       << "\n";
  sstr << "\n";
  sstr << "\n";

  sstr << "
Index of "
       << path << "
\n";
  sstr << "
";
  sstr << "
\n";

  sstr << "
"
          "Name	"
          "Size	"
          "Mime type	"
          "
\n";
}

void DirlistingModule::appendDirectory(Buffer& sstr, const std::string& filename) {
  sstr << "\t
\n";
  sstr << "\t\t<a href='"
       << filename << "/'>" << filename << "/	\n"
       << "\t\tdirectory	"
       << "	\n";
  sstr << "\t
\n";
}

void DirlistingModule::appendFile(Buffer& sstr, std::shared_ptr<File> file) {
  sstr << "\t
\n";
  sstr << "\t\t<a href='" << file->filename() << "'>"
       << file->filename() << "	\n";
  sstr << "\t\t" << file->size() << "	\n";
  sstr << "\t\t" << file->mimetype() << "	\n";
  sstr << "\t
\n";
}

void DirlistingModule::appendTrailer(Buffer& sstr) {
  sstr << "
\n";
  sstr << "
\n";
  sstr << "\n";
}

} // namespace x0d
<commit_msg>[x0d] dirlisting: adds support for generating JSON responses<commit_after>// This file is part of the "x0" project, http://github.com/christianparpart/x0>
//   (c) 2009-2017 Christian Parpart <christian@parpart.family>
//
// Licensed under the MIT License (the "License"); you may not use this
// file except in compliance with the License. You may obtain a copy of
// the License at: http://opensource.org/licenses/MIT

#include "dirlisting.h"
#include <xzero/http/HttpRequest.h>
#include <xzero/http/HttpResponse.h>
#include <xzero/io/FileUtil.h>
#include <xzero/io/File.h>
#include <xzero/JsonWriter.h>
#include <xzero/logging.h>

using namespace xzero;
using namespace xzero::http;

namespace x0d {

class OutputFormatter {
 public:
  virtual ~OutputFormatter() {}

  virtual void generateHeader(const std::string& path) = 0;
  virtual void generateEntry(std::shared_ptr<File> file) = 0;
  virtual void generateTrailer() = 0;
};

class JsonFormatter : public OutputFormatter { // {{{
 public:
  HttpResponse* response_;
  Buffer buffer_;
  JsonWriter writer_;

  explicit JsonFormatter(HttpResponse* resp)
      : response_(resp), buffer_(), writer_(&buffer_) {}

  void generateHeader(const std::string& path) override {
    writer_.beginArray("");
  }

  void generateEntry(std::shared_ptr<File> file) override {
    writer_.beginObject();

    writer_.name("filename").value(file->filename());
    writer_.name("type").value(file->isDirectory() ? "directory"
                                                   : "file");
    writer_.name("mimetype").value(file->mimetype());
    writer_.name("last-modified").value(file->lastModified());
    writer_.name("mtime").value(file->mtime());
    writer_.name("size").value(file->isDirectory() ? 0 : file->size());
    writer_.endObject();
  }

  void generateTrailer() override {
    writer_.endArray();

    response_->setContentLength(buffer_.size());
    response_->setHeader("Content-Type", "application/json");
    response_->write(std::move(buffer_));
  }
};
// }}}

class HtmlFormatter : public OutputFormatter { // {{{
 public:
  Buffer buffer_;
  HttpResponse* response_;

  explicit HtmlFormatter(HttpResponse* resp) : response_(resp) {}

  void generateEntry(std::shared_ptr<File> file) override {
    if (file->isDirectory()) {
      appendDirectory(file->filename());
    } else if (file->isRegular()) {
      appendFile(file);
    }
  }

  void generateHeader(const std::string& path) override {
    buffer_ << ""
            << "<title>Directory: " << path << "</title>"
            << "\n";
    buffer_ << "\n";
    buffer_ << "\n";

    buffer_ << "
Index of "
            << path << "
\n";
    buffer_ << "
";
    buffer_ << "
\n";

    buffer_ << "
"
               "Name	"
               "Size	"
               "Mime type	"
               "
\n";

    if (path != "/") {
      appendDirectory("..");
    }
  }

  void appendDirectory(const std::string& filename) {
    buffer_ << "\t
\n";
    buffer_ << "\t\t<a href='"
            << filename << "/'>" << filename << "/	\n"
            << "\t\tdirectory	"
            << "	\n";
    buffer_ << "\t
\n";
  }

  void appendFile(std::shared_ptr<File> file) {
    buffer_ << "\t
\n";
    buffer_ << "\t\t<a href='" << file->filename() << "'>"
            << file->filename() << "	\n";
    buffer_ << "\t\t" << file->size() << "	\n";
    buffer_ << "\t\t" << file->mimetype() << "	\n";
    buffer_ << "\t
\n";
  }

  void generateTrailer() override {
    buffer_ << "
\n";
    buffer_ << "
\n";
    buffer_ << "\n";

    response_->setContentLength(buffer_.size());
    response_->setHeader("Content-Type", "text/html");
    response_->write(std::move(buffer_));
  }
}; // }}}

DirlistingModule::DirlistingModule(x0d::XzeroDaemon* d)
    : XzeroModule(d, "dirlisting") {

  mainHandler("dirlisting", &DirlistingModule::dirlisting);
}

bool DirlistingModule::dirlisting(XzeroContext* cx, Params& args) {
  if (!cx->verifyDirectoryDepth())
    return true;

  if (!cx->file()) {
    logError("dirlisting", "Requesth path not mapped to a physical location yet.");
    return false;
  }

  if (!cx->file()->isDirectory())
    return false;

  std::unique_ptr<OutputFormatter> formatter;

  // TODO: properly parse Accept header (but yeah)
  std::string accept = cx->request()->getHeader("Accept");

  if (accept == "application/json") {
    formatter = std::make_unique<JsonFormatter>(cx->response());
  } else {
    // default to text/html
    formatter = std::make_unique<HtmlFormatter>(cx->response());
  }

  formatter->generateHeader(cx->request()->path());

  FileUtil::ls(cx->file()->path(), [&](const std::string& path) -> bool {
    formatter->generateEntry(daemon().vfs().getFile(path));
    return true;
  });

  cx->response()->setStatus(HttpStatus::Ok);
  formatter->generateTrailer();
  cx->response()->completed();

  return true;
}

} // namespace x0d
<|endoftext|>


<commit_before>/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */

#include "smmod.hxx"
#include "tmpdevice.hxx"

// SmTmpDevice
// Allows for font and color changes. The original settings will be restored
// in the destructor.
// It's main purpose is to allow for the "const" in the 'OutputDevice'
// argument in the 'Arrange' functions and restore changes made in the 'Draw'
// functions.
// Usually a MapMode of 1/100th mm will be used.

SmTmpDevice::SmTmpDevice(OutputDevice &rTheDev, bool bUseMap100th_mm) :
    rOutDev(rTheDev)
{
    rOutDev.Push( PUSH_FONT | PUSH_MAPMODE |
                  PUSH_LINECOLOR | PUSH_FILLCOLOR | PUSH_TEXTCOLOR );
    if (bUseMap100th_mm  &&  MAP_100TH_MM != rOutDev.GetMapMode().GetMapUnit())
    {
        SAL_WARN("starmath", "incorrect MapMode?");
        rOutDev.SetMapMode( MAP_100TH_MM );     //format for 100% always
    }
}

Color SmTmpDevice::Impl_GetColor( const Color& rColor )
{
    ColorData nNewCol = rColor.GetColor();
    if (COL_AUTO == nNewCol)
    {
        if (OUTDEV_PRINTER == rOutDev.GetOutDevType())
            nNewCol = COL_BLACK;
        else
        {
            Color aBgCol( rOutDev.GetBackground().GetColor() );
            if (OUTDEV_WINDOW == rOutDev.GetOutDevType())
                aBgCol = ((Window &) rOutDev).GetDisplayBackground().GetColor();

            nNewCol = SM_MOD()->GetColorConfig().GetColorValue(svtools::FONTCOLOR).nColor;

            Color aTmpColor( nNewCol );
            if (aBgCol.IsDark() && aTmpColor.IsDark())
                nNewCol = COL_WHITE;
            else if (aBgCol.IsBright() && aTmpColor.IsBright())
                nNewCol = COL_BLACK;
        }
    }
    return Color( nNewCol );
}

void SmTmpDevice::SetFont(const Font &rNewFont)
{
    rOutDev.SetFont( rNewFont );
    rOutDev.SetTextColor( Impl_GetColor( rNewFont.GetColor() ) );
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
<commit_msg>missing include<commit_after>/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */

#include "smmod.hxx"
#include "tmpdevice.hxx"

#include <vcl/window.hxx>

// SmTmpDevice
// Allows for font and color changes. The original settings will be restored
// in the destructor.
// It's main purpose is to allow for the "const" in the 'OutputDevice'
// argument in the 'Arrange' functions and restore changes made in the 'Draw'
// functions.
// Usually a MapMode of 1/100th mm will be used.

SmTmpDevice::SmTmpDevice(OutputDevice &rTheDev, bool bUseMap100th_mm) :
    rOutDev(rTheDev)
{
    rOutDev.Push( PUSH_FONT | PUSH_MAPMODE |
                  PUSH_LINECOLOR | PUSH_FILLCOLOR | PUSH_TEXTCOLOR );
    if (bUseMap100th_mm  &&  MAP_100TH_MM != rOutDev.GetMapMode().GetMapUnit())
    {
        SAL_WARN("starmath", "incorrect MapMode?");
        rOutDev.SetMapMode( MAP_100TH_MM );     //format for 100% always
    }
}

Color SmTmpDevice::Impl_GetColor( const Color& rColor )
{
    ColorData nNewCol = rColor.GetColor();
    if (COL_AUTO == nNewCol)
    {
        if (OUTDEV_PRINTER == rOutDev.GetOutDevType())
            nNewCol = COL_BLACK;
        else
        {
            Color aBgCol( rOutDev.GetBackground().GetColor() );
            if (OUTDEV_WINDOW == rOutDev.GetOutDevType())
                aBgCol = ((Window &) rOutDev).GetDisplayBackground().GetColor();

            nNewCol = SM_MOD()->GetColorConfig().GetColorValue(svtools::FONTCOLOR).nColor;

            Color aTmpColor( nNewCol );
            if (aBgCol.IsDark() && aTmpColor.IsDark())
                nNewCol = COL_WHITE;
            else if (aBgCol.IsBright() && aTmpColor.IsBright())
                nNewCol = COL_BLACK;
        }
    }
    return Color( nNewCol );
}

void SmTmpDevice::SetFont(const Font &rNewFont)
{
    rOutDev.SetFont( rNewFont );
    rOutDev.SetTextColor( Impl_GetColor( rNewFont.GetColor() ) );
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
<|endoftext|>


<commit_before>/*
 *  transfer.hpp
 *
 *
 *  Created by Andrea Bedinion 13/Jul/2009.
 *  Copyright 2009, 2010, 2011 Andrea Bedini.
 *
 *  Distributed under the terms of the GNU General Public License.
 *  The full license is in the file COPYING, distributed as part of
 *  this software.
 *
 */

#ifndef TRANSFER_HPP
#define TRANSFER_HPP

#include "tree_decomposition/tree_decomposition.hpp"

#include <boost/range/algorithm/set_algorithm.hpp>

namespace transfer {
  // Tree decomposition typedefs
  typedef tree_decomposition::tree_decomposition tree_type;
  typedef tree_decomposition::bag                bag_type;

  template<class Operators>
  typename Operators::table_type
  recurse(const Operators& op, const tree_type& t, tree_type::iterator ti)
  {
    // give an easy name to this bag
    bag_type const& b(*ti);

    // create a new table containing only the empty state
    const unsigned int n = ti->vertices.size();
    typename Operators::table_type table = op.empty_state(n);

    // iterates over children
    tree_type::sibling_iterator sib, sib_end;
    for (sib = t.begin(ti), sib_end = t.end(ti); sib != sib_end; ++sib) {
      // recurse
      typename Operators::table_type table_sib = recurse(op, t, sib);

      // make a copy of this bag since we don't want to touch the tree
      // decomposition
      bag_type b_sib(*sib);

      // diffe contains the vertices in b_sib which are not in b (the parent bag)
      std::vector<unsigned int> diffe;
      boost::set_difference(b_sib.vertices, b.vertices, std::back_inserter(diffe));

      // delete each vertex not present in the parent bag
      BOOST_FOREACH(unsigned int v, diffe) {
	table_sib = op.delete_operator(b_sib.vertices.index(v), table_sib);
	b_sib.vertices.remove(v);
      }

      // create b_sib to b bag mapping
      const unsigned int A_size = b_sib.vertices.size();
      std::vector<unsigned int> A_to_B(A_size);
      for (unsigned int i = 0; i < A_size; ++i)
	A_to_B[i] = b.vertices.index(b_sib.vertices.at(i));

      table = op.table_fusion(A_to_B, table_sib, table);
    }

    // apply the join operator for each edge in the bag
    std::pair<unsigned int, unsigned int> e;
    BOOST_FOREACH(e, b.edges) {
      table = op.join_operator(b.vertices.index(e.first),
			       b.vertices.index(e.second), table);
    }
    return table;
  }

  template<class Operators>
  typename Operators::weight_type
  transfer(const Operators& op, const tree_type& t)
  {
    tree_type::iterator ti = t.begin();
    typename Operators::table_type table = recurse(op, t, ti);

    // make a copy of this bag since we don't want to touch the tree
    // decomposition
    bag_type b(*ti);

    BOOST_FOREACH(unsigned int v, ti->vertices) {
      table = op.delete_operator(b.vertices.index(v), table);
      b.vertices.remove(v);
    }
    assert(table.size() == 1);
    return table.begin()->second;
  }
}

#endif
<commit_msg>Add missing #include.<commit_after>/*
 *  transfer.hpp
 *
 *
 *  Created by Andrea Bedinion 13/Jul/2009.
 *  Copyright 2009, 2010, 2011 Andrea Bedini.
 *
 *  Distributed under the terms of the GNU General Public License.
 *  The full license is in the file COPYING, distributed as part of
 *  this software.
 *
 */

#ifndef TRANSFER_HPP
#define TRANSFER_HPP

#include "tree_decomposition/tree_decomposition.hpp"
#include <boost/foreach.hpp>
#include <boost/range/algorithm/set_algorithm.hpp>

namespace transfer {
  // Tree decomposition typedefs
  typedef tree_decomposition::tree_decomposition tree_type;
  typedef tree_decomposition::bag                bag_type;

  template<class Operators>
  typename Operators::table_type
  recurse(const Operators& op, const tree_type& t, tree_type::iterator ti)
  {
    // give an easy name to this bag
    bag_type const& b(*ti);

    // create a new table containing only the empty state
    const unsigned int n = ti->vertices.size();
    typename Operators::table_type table = op.empty_state(n);

    // iterates over children
    tree_type::sibling_iterator sib, sib_end;
    for (sib = t.begin(ti), sib_end = t.end(ti); sib != sib_end; ++sib) {
      // recurse
      typename Operators::table_type table_sib = recurse(op, t, sib);

      // make a copy of this bag since we don't want to touch the tree
      // decomposition
      bag_type b_sib(*sib);

      // diffe contains the vertices in b_sib which are not in b (the parent bag)
      std::vector<unsigned int> diffe;
      boost::set_difference(b_sib.vertices, b.vertices, std::back_inserter(diffe));

      // delete each vertex not present in the parent bag
      BOOST_FOREACH(unsigned int v, diffe) {
	table_sib = op.delete_operator(b_sib.vertices.index(v), table_sib);
	b_sib.vertices.remove(v);
      }

      // create b_sib to b bag mapping
      const unsigned int A_size = b_sib.vertices.size();
      std::vector<unsigned int> A_to_B(A_size);
      for (unsigned int i = 0; i < A_size; ++i)
	A_to_B[i] = b.vertices.index(b_sib.vertices.at(i));

      table = op.table_fusion(A_to_B, table_sib, table);
    }

    // apply the join operator for each edge in the bag
    std::pair<unsigned int, unsigned int> e;
    BOOST_FOREACH(e, b.edges) {
      table = op.join_operator(b.vertices.index(e.first),
			       b.vertices.index(e.second), table);
    }
    return table;
  }

  template<class Operators>
  typename Operators::weight_type
  transfer(const Operators& op, const tree_type& t)
  {
    tree_type::iterator ti = t.begin();
    typename Operators::table_type table = recurse(op, t, ti);

    // make a copy of this bag since we don't want to touch the tree
    // decomposition
    bag_type b(*ti);

    BOOST_FOREACH(unsigned int v, ti->vertices) {
      table = op.delete_operator(b.vertices.index(v), table);
      b.vertices.remove(v);
    }
    assert(table.size() == 1);
    return table.begin()->second;
  }
}

#endif
<|endoftext|>


<commit_before>#include "model/obj.hpp"

#include <string>
#include <iostream>
#include <fstream>
#include <memory>
//#include <sstream>
#include <boost/tokenizer.hpp>
#include <boost/lexical_cast.hpp>

namespace Model {
	e_el obj_t::parse_type(std::string word) {
		if(word == element_t<e_el_c>::prefix) return e_el_c;
		if(word == element_t<e_el_f>::prefix) return e_el_f;
		if(word == element_t<e_el_g>::prefix) return e_el_g;
		if(word == element_t<e_el_l>::prefix) return e_el_l;
		if(word == element_t<e_el_m>::prefix) return e_el_m;
		if(word == element_t<e_el_o>::prefix) return e_el_o;
		if(word == element_t<e_el_u>::prefix) return e_el_u;
		if(word == element_t<e_el_s>::prefix) return e_el_s;
		if(word == element_t<e_el_v>::prefix) return e_el_v;
		if(word == element_t<e_el_vn>::prefix) return e_el_vn;
		if(word == element_t<e_el_vp>::prefix) return e_el_vp;
		return e_el_total;
	}

	obj_t::e_status obj_t::parse(std::string line, const char *delim) {
		auto status = obj_t::e_status::e_ok;
		boost::tokenizer<boost::char_separator<char>> tk(line,
			boost::char_separator<char>(delim));
		for(auto it = std::begin(tk); it != std::end(tk); ++it) {
			auto word = *it++;
			auto type = parse_type(word);
			if((mask_has_strings & (1<<type)) != 0) {
				std::ostringstream oss;
				while(it != std::end(tk)) {
					oss << *it++ << " ";
				}
				strings.emplace_back(oss.str());
				nBools.emplace_back(0);
				nFloats.emplace_back(0);
				nInts.emplace_back(0);
				nStrings.emplace_back(1);
				types.emplace_back(type);
				break;
			} else if((mask_has_ints & (1<<type)) != 0) {
				int index, nIndices = 0;
				while(it != std::end(tk)) {
					index = boost::lexical_cast<int>(*it++);
					ints.push_back(index);
					nIndices++;
				}
				nBools.emplace_back(0);
				nFloats.emplace_back(0);
				nInts.emplace_back(nIndices);
				nStrings.emplace_back(0);
				types.emplace_back(type);
				break;
			} else if((mask_has_floats & (1<<type)) != 0) {
				float val;
				int nValues = 0;
				while(it != std::end(tk)) {
					val = boost::lexical_cast<float>(*it++);
					floats.push_back(val);
					nValues++;
				}
				nBools.emplace_back(0);
				nFloats.emplace_back(nValues);
				nInts.emplace_back(0);
				nStrings.emplace_back(0);
				types.emplace_back(type);
			} else if(type == e_el_s) {
				if(it == std::end(tk)) {
					bools.emplace_back(true);
				} else {
					auto word = *it++;
					bools.emplace_back(word == "1" || word == "on");
				}
				nBools.emplace_back(1);
				nFloats.emplace_back(0);
				nInts.emplace_back(0);
				nStrings.emplace_back(0);
				types.emplace_back(type);
			} else if(type < e_el_total && type >= e_el_c) {
				nBools.emplace_back(0);
				nFloats.emplace_back(0);
				nInts.emplace_back(0);
				nStrings.emplace_back(0);
			} else {
				status = e_err_unknown;
			}
			break;
		}
		return status;
	}

	obj_t::e_status obj_t::load(const char *fname, obj_t &obj) {
		std::ifstream file;
		file.open(fname, std::ios::in);
		if(!file.is_open()) {
			return e_err_io;
		}
		e_el element;
		e_status status = e_ok;

		for(std::string line; std::getline(file, line);) {
			auto st = obj.parse(line, " /");
			if(status == e_ok) status = st;
			// TODO Break or no?
		}

		file.close();
		return status;
	}
}
<commit_msg>Changed logic to allow for multi-type parsing; TODO replace greedy line parse<commit_after>#include "model/obj.hpp"

#include <string>
#include <iostream>
#include <fstream>
#include <memory>
//#include <sstream>
#include <boost/tokenizer.hpp>
#include <boost/lexical_cast.hpp>

namespace Model {
	e_el obj_t::parse_type(std::string word) {
		if(word == element_t<e_el_c>::prefix) return e_el_c;
		if(word == element_t<e_el_f>::prefix) return e_el_f;
		if(word == element_t<e_el_g>::prefix) return e_el_g;
		if(word == element_t<e_el_l>::prefix) return e_el_l;
		if(word == element_t<e_el_m>::prefix) return e_el_m;
		if(word == element_t<e_el_o>::prefix) return e_el_o;
		if(word == element_t<e_el_u>::prefix) return e_el_u;
		if(word == element_t<e_el_s>::prefix) return e_el_s;
		if(word == element_t<e_el_v>::prefix) return e_el_v;
		if(word == element_t<e_el_vn>::prefix) return e_el_vn;
		if(word == element_t<e_el_vp>::prefix) return e_el_vp;
		return e_el_total;
	}

	obj_t::e_status obj_t::parse(std::string line, const char *delim) {
		auto status = obj_t::e_status::e_ok;
		boost::tokenizer<boost::char_separator<char>> tk(line,
			boost::char_separator<char>(delim));
		for(auto it = std::begin(tk); it != std::end(tk); ++it) {
			auto word = *it++;
			auto type = parse_type(word);
			auto has_strings=false, has_floats=false,
				 has_ints = false, has_bools = false;
			if((mask_has_strings & (1<<type)) != 0) {
				has_strings = true;
				std::ostringstream oss;
				while(it != std::end(tk)) {
					oss << *it++ << " ";
				}
				strings.emplace_back(oss.str());
				nStrings.emplace_back(1);
				//break;
			} else {
				nStrings.emplace_back(0);
			}
			if((mask_has_ints & (1<<type)) != 0) {
				has_ints = true;
				int index, nIndices = 0;
				while(it != std::end(tk)) {
					index = boost::lexical_cast<int>(*it++);
					ints.push_back(index);
					nIndices++;
				}
				nInts.emplace_back(nIndices);
				//break;
			} else {
				nInts.emplace_back(0);
			}
			if((mask_has_floats & (1<<type)) != 0) {
				has_floats = true;
				float val;
				int nValues = 0;
				while(it != std::end(tk)) {
					val = boost::lexical_cast<float>(*it++);
					floats.push_back(val);
					nValues++;
				}
				nFloats.emplace_back(nValues);
			} else {
				nFloats.emplace_back(0);
			}
			if(type == e_el_s) {
				has_bools = true;
				if(it == std::end(tk)) {
					bools.emplace_back(true);
				} else {
					auto word = *it++;
					bools.emplace_back(word == "1" || word == "on");
				}
				nBools.emplace_back(1);
			} else {
				nBools.emplace_back(0);
			}
			if(!has_bools && !has_floats && !has_ints && !has_strings
					&& type < e_el_total && type >= e_el_c) {
				nBools.emplace_back(0);
				nFloats.emplace_back(0);
				nInts.emplace_back(0);
				nStrings.emplace_back(0);
			} else {
				status = e_err_unknown;
			}
			types.emplace_back(type);
			break;
		}
		return status;
	}

	obj_t::e_status obj_t::load(const char *fname, obj_t &obj) {
		std::ifstream file;
		file.open(fname, std::ios::in);
		if(!file.is_open()) {
			return e_err_io;
		}
		e_el element;
		e_status status = e_ok;

		for(std::string line; std::getline(file, line);) {
			auto st = obj.parse(line, " /");
			if(status == e_ok) status = st;
			// TODO Break or no?
		}

		file.close();
		return status;
	}
}
<|endoftext|>


<commit_before>/** \copyright
 * Copyright (c) 2013, Balazs Racz
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are  permitted provided that the following conditions are met:
 *
 *  - Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 *  - Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * \file main.cxx
 *
 * An application that sends an OpenLCB datagram from command line.
 *
 * @author Balazs Racz
 * @date 27 Feb 2015
 */

#include <fcntl.h>
#include <getopt.h>
#include <stdio.h>
#include <unistd.h>

#include <memory>

#include "os/os.h"
#include "utils/constants.hxx"
#include "utils/Hub.hxx"
#include "utils/GridConnectHub.hxx"
#include "utils/GcTcpHub.hxx"
#include "utils/Crc.hxx"
#include "executor/Executor.hxx"
#include "executor/Service.hxx"

#include "nmranet/IfCan.hxx"
#include "nmranet/DatagramCan.hxx"
#include "nmranet/BootloaderClient.hxx"
#include "nmranet/If.hxx"
#include "nmranet/AliasAllocator.hxx"
#include "nmranet/DefaultNode.hxx"
#include "utils/socket_listener.hxx"

#include "freertos/bootloader_hal.h"

NO_THREAD nt;
Executor<1> g_executor(nt);
Service g_service(&g_executor);
CanHubFlow can_hub0(&g_service);

static const nmranet::NodeID NODE_ID = 0x05010101181EULL;

nmranet::IfCan g_if_can(&g_executor, &can_hub0, 3, 3, 2);
nmranet::CanDatagramService g_datagram_can(&g_if_can, 10, 2);
static nmranet::AddAliasAllocator g_alias_allocator(NODE_ID, &g_if_can);
nmranet::DefaultNode g_node(&g_if_can, NODE_ID);

namespace nmranet
{
Pool *const g_incoming_datagram_allocator = mainBufferPool;
}

int port = 12021;
const char *host = "localhost";
const char *device_path = nullptr;
const char *filename = nullptr;
uint64_t destination_nodeid = 0;
unsigned destination_alias = 0;
string payload_string;

void usage(const char *e)
{
    fprintf(stderr,
        "Usage: %s ([-i destination_host] [-p port] | [-d device_path]) "
        "(-n nodeid | -a alias) -g payload_hex\n",
        e);
    fprintf(stderr, "Connects to an openlcb bus and sends a datagram to a "
                    "specific node on the bus.\n");
    fprintf(stderr,
        "The bus connection will be through an OpenLCB HUB on "
        "destination_host:port with OpenLCB over TCP "
        "(in GridConnect format) protocol, or through the CAN-USB device "
        "(also in GridConnect protocol) found at device_path. Device takes "
        "precedence over TCP host:port specification.");
    fprintf(stderr, "The default target is localhost:12021.\n");
    fprintf(stderr, "\nnodeid should be a 12-char hex string with 0x prefix "
                    "and no separators, like '-b 0x05010101141F'\n");
    fprintf(stderr, "\nalias should be a 3-char hex string with 0x prefix and "
                    "no separators, like '-a 0x3F9'\n");
    fprintf(stderr, "\npayload is a string of hex digits that define the "
                    "datagram payload. This payload usually starts with the "
                    "datagram type byte. Required. Example: '-d 20A9' to send "
                    "a memory config datagram with reboot request.\n");
    exit(1);
}

void parse_args(int argc, char *argv[])
{
    int opt;
    while ((opt = getopt(argc, argv, "hi:p:d:n:a:g:")) >= 0)
    {
        switch (opt)
        {
            case 'h':
                usage(argv[0]);
                break;
            case 'i':
                host = optarg;
                break;
            case 'p':
                port = atoi(optarg);
                break;
            case 'd':
                device_path = optarg;
                break;
            case 'n':
                destination_nodeid = strtoll(optarg, nullptr, 16);
                break;
            case 'a':
                destination_alias = strtoul(optarg, nullptr, 16);
                break;
            case 'g':
                payload_string = optarg;
                break;
            default:
                fprintf(stderr, "Unknown option %c\n", opt);
                usage(argv[0]);
        }
    }
    if (payload_string.empty() || (!destination_nodeid && !destination_alias))
    {
        usage(argv[0]);
    }
}

using nmranet::DatagramPayload;
using nmranet::DatagramClient;
using nmranet::Defs;
using nmranet::NodeHandle;

/** Entry point to application.
 * @param argc number of command line arguments
 * @param argv array of command line arguments
 * @return 0, should never return
 */
int appl_main(int argc, char *argv[])
{
    parse_args(argc, argv);
    int conn_fd = 0;
    if (device_path)
    {
        conn_fd = ::open(device_path, O_RDWR);
    }
    else
    {
        conn_fd = ConnectSocket(host, port);
    }
    HASSERT(conn_fd >= 0);
    create_gc_port_for_can_hub(&can_hub0, conn_fd);

    g_if_can.add_addressed_message_support();
    // Bootstraps the alias allocation process.
    g_if_can.alias_allocator()->send(g_if_can.alias_allocator()->alloc());

    g_executor.start_thread("g_executor", 0, 1024);

    // Waits for stack to be up.
    while (!g_node.is_initialized()) usleep(10000);

    // Parses datagram into a payload.
    DatagramPayload payload;
    unsigned ofs = 0;
    while (ofs + 2 <= payload_string.size()) {
        payload.push_back(
            strtoul(payload_string.substr(ofs, 2).c_str(), nullptr, 16));
        ofs += 2;
    }

    SyncNotifiable n;
    BarrierNotifiable bn(&n);

    DatagramClient* client = g_datagram_can.client_allocator()->next_blocking();
    Buffer<nmranet::NMRAnetMessage> *b;
    mainBufferPool->alloc(&b);

    NodeHandle dst;
    dst.alias = destination_alias;
    dst.id = destination_nodeid;
    b->data()->reset(Defs::MTI_DATAGRAM, g_node.node_id(),
                     dst, payload);
    b->set_done(&bn);

    client->write_datagram(b);
    n.wait_for_notification();

    fprintf(stderr, "Datagram send result: %04x\n", client->result());

    g_datagram_can.client_allocator()->typed_insert(client);
    return 0;
}
<commit_msg>Allows sending multiple datagrams in a single execution.<commit_after>/** \copyright
 * Copyright (c) 2013, Balazs Racz
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are  permitted provided that the following conditions are met:
 *
 *  - Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 *  - Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * \file main.cxx
 *
 * An application that sends an OpenLCB datagram from command line.
 *
 * @author Balazs Racz
 * @date 27 Feb 2015
 */

#include <fcntl.h>
#include <getopt.h>
#include <stdio.h>
#include <unistd.h>

#include <memory>

#include "os/os.h"
#include "utils/constants.hxx"
#include "utils/Hub.hxx"
#include "utils/GridConnectHub.hxx"
#include "utils/GcTcpHub.hxx"
#include "utils/Crc.hxx"
#include "executor/Executor.hxx"
#include "executor/Service.hxx"

#include "nmranet/IfCan.hxx"
#include "nmranet/DatagramCan.hxx"
#include "nmranet/BootloaderClient.hxx"
#include "nmranet/If.hxx"
#include "nmranet/AliasAllocator.hxx"
#include "nmranet/DefaultNode.hxx"
#include "utils/socket_listener.hxx"

#include "freertos/bootloader_hal.h"

NO_THREAD nt;
Executor<1> g_executor(nt);
Service g_service(&g_executor);
CanHubFlow can_hub0(&g_service);

static const nmranet::NodeID NODE_ID = 0x05010101181EULL;

nmranet::IfCan g_if_can(&g_executor, &can_hub0, 3, 3, 2);
nmranet::CanDatagramService g_datagram_can(&g_if_can, 10, 2);
static nmranet::AddAliasAllocator g_alias_allocator(NODE_ID, &g_if_can);
nmranet::DefaultNode g_node(&g_if_can, NODE_ID);

namespace nmranet
{
Pool *const g_incoming_datagram_allocator = mainBufferPool;
}

int port = 12021;
const char *host = "localhost";
const char *device_path = nullptr;
const char *filename = nullptr;
uint64_t destination_nodeid = 0;
unsigned destination_alias = 0;
vector<string> payload_strings;

void usage(const char *e)
{
    fprintf(stderr,
        "Usage: %s ([-i destination_host] [-p port] | [-d device_path]) "
        "(-n nodeid | -a alias) (-g payload_hex)...\n",
        e);
    fprintf(stderr, "Connects to an openlcb bus and sends a datagram to a "
                    "specific node on the bus.\n");
    fprintf(stderr,
        "The bus connection will be through an OpenLCB HUB on "
        "destination_host:port with OpenLCB over TCP "
        "(in GridConnect format) protocol, or through the CAN-USB device "
        "(also in GridConnect protocol) found at device_path. Device takes "
        "precedence over TCP host:port specification.");
    fprintf(stderr, "The default target is localhost:12021.\n");
    fprintf(stderr, "\nnodeid should be a 12-char hex string with 0x prefix "
                    "and no separators, like '-b 0x05010101141F'\n");
    fprintf(stderr, "\nalias should be a 3-char hex string with 0x prefix and "
                    "no separators, like '-a 0x3F9'\n");
    fprintf(stderr, "\npayload is a string of hex digits that define the "
                    "datagram payload. This payload usually starts with the "
                    "datagram type byte. Required. Example: '-g 20A9' to send "
                    "a memory config datagram with reboot request. "
                    "You can supply multiple -g arguments to send multiple "
                    "datagrams\n");
    exit(1);
}

void parse_args(int argc, char *argv[])
{
    int opt;
    while ((opt = getopt(argc, argv, "hi:p:d:n:a:g:")) >= 0)
    {
        switch (opt)
        {
            case 'h':
                usage(argv[0]);
                break;
            case 'i':
                host = optarg;
                break;
            case 'p':
                port = atoi(optarg);
                break;
            case 'd':
                device_path = optarg;
                break;
            case 'n':
                destination_nodeid = strtoll(optarg, nullptr, 16);
                break;
            case 'a':
                destination_alias = strtoul(optarg, nullptr, 16);
                break;
            case 'g':
                payload_strings.push_back(optarg);
                break;
            default:
                fprintf(stderr, "Unknown option %c\n", opt);
                usage(argv[0]);
        }
    }
    if (payload_strings.empty() || (!destination_nodeid && !destination_alias))
    {
        usage(argv[0]);
    }
}

using nmranet::DatagramPayload;
using nmranet::DatagramClient;
using nmranet::Defs;
using nmranet::NodeHandle;

/** Entry point to application.
 * @param argc number of command line arguments
 * @param argv array of command line arguments
 * @return 0, should never return
 */
int appl_main(int argc, char *argv[])
{
    parse_args(argc, argv);
    int conn_fd = 0;
    if (device_path)
    {
        conn_fd = ::open(device_path, O_RDWR);
    }
    else
    {
        conn_fd = ConnectSocket(host, port);
    }
    HASSERT(conn_fd >= 0);
    create_gc_port_for_can_hub(&can_hub0, conn_fd);

    g_if_can.add_addressed_message_support();
    // Bootstraps the alias allocation process.
    g_if_can.alias_allocator()->send(g_if_can.alias_allocator()->alloc());

    g_executor.start_thread("g_executor", 0, 1024);

    // Waits for stack to be up.
    while (!g_node.is_initialized())
        usleep(10000);
    LOG(INFO, "Node initialized.");

    // Parses datagram into a payload.
    SyncNotifiable n;
    BarrierNotifiable bn;

    DatagramClient *client = g_datagram_can.client_allocator()->next_blocking();

    for (const auto &payload_string : payload_strings)
    {
        LOG(INFO, "Sending datagram %s", payload_string.c_str());
        DatagramPayload payload;
        unsigned ofs = 0;
        while (ofs + 2 <= payload_string.size())
        {
            payload.push_back(
                strtoul(payload_string.substr(ofs, 2).c_str(), nullptr, 16));
            ofs += 2;
        }
        Buffer<nmranet::NMRAnetMessage> *b;
        mainBufferPool->alloc(&b);

        NodeHandle dst;
        dst.alias = destination_alias;
        dst.id = destination_nodeid;
        b->data()->reset(Defs::MTI_DATAGRAM, g_node.node_id(), dst, payload);
        b->set_done(bn.reset(&n));

        client->write_datagram(b);
        n.wait_for_notification();

        fprintf(stderr, "Datagram send result: %04x\n", client->result());
    }

    g_datagram_can.client_allocator()->typed_insert(client);
    exit(0); // do not call destructors.
    return 0;
}
<|endoftext|>


<commit_before>#include <v8.h>
#include <node.h>
#include <node_object_wrap.h>
#include <queue>

#include "lib/RtMidi/RtMidi.h"
#include "lib/RtMidi/RtMidi.cpp"

using namespace node;

#define SAFE_NODE_SET_PROTOTYPE_METHOD(templ, name, callback)             \
do {                                                                      \
  v8::Local<v8::Signature> __callback##_SIG = v8::Signature::New(templ);  \
  v8::Local<v8::FunctionTemplate> __callback##_TEM =                      \
    v8::FunctionTemplate::New(callback, v8::Handle<v8::Value>(),          \
                          __callback##_SIG);                              \
  templ->PrototypeTemplate()->Set(v8::String::NewSymbol(name),            \
                                  __callback##_TEM);                      \
} while (0)

class NodeMidiOutput : ObjectWrap
{
private:
    RtMidiOut* out;
public:
    static v8::Persistent<v8::FunctionTemplate> s_ct;
    static void Init(v8::Handle<v8::Object> target)
    {
        v8::HandleScope scope;

        v8::Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(New);

        s_ct = v8::Persistent<v8::FunctionTemplate>::New(t);
        s_ct->InstanceTemplate()->SetInternalFieldCount(1);
        s_ct->SetClassName(v8::String::NewSymbol("NodeMidiOutput"));

        SAFE_NODE_SET_PROTOTYPE_METHOD(s_ct, "getPortCount", GetPortCount);
        SAFE_NODE_SET_PROTOTYPE_METHOD(s_ct, "getPortName", GetPortName);

        SAFE_NODE_SET_PROTOTYPE_METHOD(s_ct, "openPort", OpenPort);
        SAFE_NODE_SET_PROTOTYPE_METHOD(s_ct, "openVirtualPort", OpenVirtualPort);
        SAFE_NODE_SET_PROTOTYPE_METHOD(s_ct, "closePort", ClosePort);

        SAFE_NODE_SET_PROTOTYPE_METHOD(s_ct, "sendMessage", SendMessage);

        target->Set(v8::String::NewSymbol("output"),
                    s_ct->GetFunction());
    }

    NodeMidiOutput()
    {
        out = new RtMidiOut();
    }

    ~NodeMidiOutput()
    {
        delete out;
    }

    static v8::Handle<v8::Value> New(const v8::Arguments& args)
    {
        v8::HandleScope scope;
        NodeMidiOutput* output = new NodeMidiOutput();
        output->Wrap(args.This());
        return args.This();
    }

    static v8::Handle<v8::Value> GetPortCount(const v8::Arguments& args)
    {
        v8::HandleScope scope;
        NodeMidiOutput* output = ObjectWrap::Unwrap<NodeMidiOutput>(args.This());
        v8::Local<v8::Integer> result = v8::Uint32::New(output->out->getPortCount());
        return scope.Close(result);
    }

    static v8::Handle<v8::Value> GetPortName(const v8::Arguments& args)
    {
        v8::HandleScope scope;
        NodeMidiOutput* output = ObjectWrap::Unwrap<NodeMidiOutput>(args.This());
        if (args.Length() == 0 || !args[0]->IsUint32()) {
            return ThrowException(v8::Exception::TypeError(
                v8::String::New("First argument must be an integer")));
        }
        unsigned int portNumber = args[0]->Uint32Value();
        v8::Local<v8::String> result = v8::String::New(output->out->getPortName(portNumber).c_str());
        return scope.Close(result);
    }

    static v8::Handle<v8::Value> OpenPort(const v8::Arguments& args)
    {
        v8::HandleScope scope;
        NodeMidiOutput* output = ObjectWrap::Unwrap<NodeMidiOutput>(args.This());
        if (args.Length() == 0 || !args[0]->IsUint32()) {
            return ThrowException(v8::Exception::TypeError(
                v8::String::New("First argument must be an integer")));
        }
        unsigned int portNumber = args[0]->Uint32Value();
        output->out->openPort(portNumber);
        return scope.Close(v8::Boolean::New(true));
    }

    static v8::Handle<v8::Value> OpenVirtualPort(const v8::Arguments& args)
    {
        v8::HandleScope scope;
        NodeMidiOutput* output = ObjectWrap::Unwrap<NodeMidiOutput>(args.This());
        if (args.Length() == 0 || !args[0]->IsString()) {
            return ThrowException(v8::Exception::TypeError(
                v8::String::New("First argument must be a string")));
        }
        std::string name(*v8::String::AsciiValue(args[0]));
        output->out->openVirtualPort(name);
        return scope.Close(v8::Boolean::New(true));
    }

    static v8::Handle<v8::Value> ClosePort(const v8::Arguments& args)
    {
        v8::HandleScope scope;
        NodeMidiOutput* output = ObjectWrap::Unwrap<NodeMidiOutput>(args.This());
        output->out->closePort();
        return scope.Close(v8::Boolean::New(true));
    }

    static v8::Handle<v8::Value> SendMessage(const v8::Arguments& args)
    {
        v8::HandleScope scope;
        NodeMidiOutput* output = ObjectWrap::Unwrap<NodeMidiOutput>(args.This());
        if (args.Length() == 0 || !args[0]->IsArray()) {
            return ThrowException(v8::Exception::TypeError(
                v8::String::New("First argument must be an array")));
        }
        v8::Local<v8::Object> message = args[0]->ToObject();
        size_t messageLength = message->Get(v8::String::New("length"))->Int32Value();
        std::vector<unsigned char> messageOutput;
        for (size_t i = 0; i != messageLength; ++i) {
            messageOutput.push_back(message->Get(v8::Integer::New(i))->Int32Value());
        }
        output->out->sendMessage(&messageOutput);
        return scope.Close(v8::Boolean::New(true));
    }
};

static v8::Persistent<v8::String> emit_symbol;

class NodeMidiInput : public ObjectWrap
{
private:
    RtMidiIn* in;

public:
    ev_async* message_async;
    pthread_mutex_t message_mutex;

    struct MidiMessage
    {
        double deltaTime;
        std::vector<unsigned char> message;
    };
    std::queue<MidiMessage*> message_queue;

    static v8::Persistent<v8::FunctionTemplate> s_ct;
    static void Init(v8::Handle<v8::Object> target)
    {
        v8::HandleScope scope;

        v8::Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(New);

        s_ct = v8::Persistent<v8::FunctionTemplate>::New(t);
        s_ct->InstanceTemplate()->SetInternalFieldCount(1);

        emit_symbol = NODE_PSYMBOL("emit");

        s_ct->SetClassName(v8::String::NewSymbol("NodeMidiInput"));

        SAFE_NODE_SET_PROTOTYPE_METHOD(s_ct, "getPortCount", GetPortCount);
        SAFE_NODE_SET_PROTOTYPE_METHOD(s_ct, "getPortName", GetPortName);

        SAFE_NODE_SET_PROTOTYPE_METHOD(s_ct, "openPort", OpenPort);
        SAFE_NODE_SET_PROTOTYPE_METHOD(s_ct, "openVirtualPort", OpenVirtualPort);
        SAFE_NODE_SET_PROTOTYPE_METHOD(s_ct, "closePort", ClosePort);

        SAFE_NODE_SET_PROTOTYPE_METHOD(s_ct, "ignoreTypes", IgnoreTypes);

        target->Set(v8::String::NewSymbol("input"),
                    s_ct->GetFunction());
    }

    NodeMidiInput()
    {
        in = new RtMidiIn();
        pthread_mutex_init(&message_mutex, NULL);
        message_async = new ev_async();
    }

    ~NodeMidiInput()
    {
        in->closePort();
        ev_async_stop(EV_DEFAULT_UC_ message_async);
        delete message_async;
        delete in;
        pthread_mutex_destroy(&message_mutex);
    }

    static void EmitMessage(EV_P_ ev_async *w, int revents)
    {
        v8::HandleScope scope;
        NodeMidiInput *input = static_cast<NodeMidiInput*>(w->data);
        pthread_mutex_lock(&input->message_mutex);
        while (!input->message_queue.empty())
        {
            MidiMessage* message = input->message_queue.front();
            v8::Local<v8::Value> args[3];
            args[0]= v8::String::New("message");
            args[1] = v8::Local<v8::Value>::New(v8::Number::New(message->deltaTime));
            size_t count = message->message.size();
            v8::Local<v8::Array> data = v8::Array::New(count);
            for (size_t i = 0; i < count; ++i) {
                data->Set(v8::Number::New(i), v8::Integer::New(message->message[i]));
            }
            args[2] = v8::Local<v8::Value>::New(data);
            v8::Local<v8::Value> emit_v = input->handle_->Get(emit_symbol);
            if (emit_v->IsFunction()) {
                v8::Local<v8::Function> emit=v8::Local<v8::Function>::Cast(emit_v);
                v8::TryCatch tc;
                emit->Call(input->handle_,3,args);
                if (tc.HasCaught()){
                    node::FatalException(tc);
               }
            }
            input->message_queue.pop();
            delete message;
        }
        pthread_mutex_unlock(&input->message_mutex);
    }

    static void Callback(double deltaTime, std::vector<unsigned char> *message, void *userData)
    {
        NodeMidiInput *input = static_cast<NodeMidiInput*>(userData);
        MidiMessage* data = new MidiMessage();
        data->deltaTime = deltaTime;
        data->message = *message;
        pthread_mutex_lock(&input->message_mutex);
        input->message_queue.push(data);
        pthread_mutex_unlock(&input->message_mutex);
        ev_async_send(EV_DEFAULT_UC_ input->message_async);
    }

    static v8::Handle<v8::Value> New(const v8::Arguments& args)
    {
        v8::HandleScope scope;
        NodeMidiInput* input = new NodeMidiInput();
        input->message_async->data = input;
        ev_async_init(input->message_async, NodeMidiInput::EmitMessage);
        ev_async_start(EV_DEFAULT_UC_ input->message_async);
        ev_unref(EV_DEFAULT_UC);
        input->Wrap(args.This());
        return args.This();
    }

    static v8::Handle<v8::Value> GetPortCount(const v8::Arguments& args)
    {
        v8::HandleScope scope;
        NodeMidiInput* input = ObjectWrap::Unwrap<NodeMidiInput>(args.This());
        v8::Local<v8::Integer> result = v8::Uint32::New(input->in->getPortCount());
        return scope.Close(result);
    }

    static v8::Handle<v8::Value> GetPortName(const v8::Arguments& args)
    {
        v8::HandleScope scope;
        NodeMidiInput* input = ObjectWrap::Unwrap<NodeMidiInput>(args.This());
        if (args.Length() == 0 || !args[0]->IsUint32()) {
            return ThrowException(v8::Exception::TypeError(
                v8::String::New("First argument must be an integer")));
        }
        unsigned int portNumber = args[0]->Uint32Value();
        v8::Local<v8::String> result = v8::String::New(input->in->getPortName(portNumber).c_str());
        return scope.Close(result);
    }

    static v8::Handle<v8::Value> OpenPort(const v8::Arguments& args)
    {
        v8::HandleScope scope;
        NodeMidiInput* input = ObjectWrap::Unwrap<NodeMidiInput>(args.This());
        if (args.Length() == 0 || !args[0]->IsUint32()) {
            return ThrowException(v8::Exception::TypeError(
                v8::String::New("First argument must be an integer")));
        }
        unsigned int portNumber = args[0]->Uint32Value();
        input->Ref();
        input->in->setCallback(&NodeMidiInput::Callback, ObjectWrap::Unwrap<NodeMidiInput>(args.This()));
        input->in->openPort(portNumber);
        return scope.Close(v8::Boolean::New(true));
    }

    static v8::Handle<v8::Value> OpenVirtualPort(const v8::Arguments& args)
    {
        v8::HandleScope scope;
        NodeMidiInput* input = ObjectWrap::Unwrap<NodeMidiInput>(args.This());
        if (args.Length() == 0 || !args[0]->IsString()) {
            return ThrowException(v8::Exception::TypeError(
                v8::String::New("First argument must be a string")));
        }
        std::string name(*v8::String::AsciiValue(args[0]));
        input->Ref();
        input->in->setCallback(&NodeMidiInput::Callback, ObjectWrap::Unwrap<NodeMidiInput>(args.This()));
        input->in->openVirtualPort(name);
        return scope.Close(v8::Boolean::New(true));
    }

    static v8::Handle<v8::Value> ClosePort(const v8::Arguments& args)
    {
        v8::HandleScope scope;
        NodeMidiInput* input = ObjectWrap::Unwrap<NodeMidiInput>(args.This());
        input->Unref();
        input->in->closePort();
        return scope.Close(v8::Boolean::New(true));
    }

    static v8::Handle<v8::Value> IgnoreTypes(const v8::Arguments& args)
    {
        v8::HandleScope scope;
        NodeMidiInput* input = ObjectWrap::Unwrap<NodeMidiInput>(args.This());
        if (args.Length() != 3 || !args[0]->IsBoolean() || !args[1]->IsBoolean() || !args[2]->IsBoolean()) {
            return ThrowException(v8::Exception::TypeError(
                v8::String::New("Arguments must be boolean")));
        }
        int filter_sysex = args[0]->BooleanValue();
        int filter_timing = args[1]->BooleanValue();
        int filter_sensing = args[2]->BooleanValue();
        input->in->ignoreTypes(filter_sysex,filter_timing,filter_sensing);
        return scope.Close(v8::Undefined());
    }
};

v8::Persistent<v8::FunctionTemplate> NodeMidiOutput::s_ct;
v8::Persistent<v8::FunctionTemplate> NodeMidiInput::s_ct;

extern "C" {
    void init (v8::Handle<v8::Object> target)
    {
        NodeMidiOutput::Init(target);
        NodeMidiInput::Init(target);
    }

    NODE_MODULE(nodemidi, init);
}
<commit_msg>Tidy up some return values - now returning undefined from functions with no return value instead of true.<commit_after>#include <v8.h>
#include <node.h>
#include <node_object_wrap.h>
#include <queue>

#include "lib/RtMidi/RtMidi.h"
#include "lib/RtMidi/RtMidi.cpp"

using namespace node;

#define SAFE_NODE_SET_PROTOTYPE_METHOD(templ, name, callback)             \
do {                                                                      \
  v8::Local<v8::Signature> __callback##_SIG = v8::Signature::New(templ);  \
  v8::Local<v8::FunctionTemplate> __callback##_TEM =                      \
    v8::FunctionTemplate::New(callback, v8::Handle<v8::Value>(),          \
                          __callback##_SIG);                              \
  templ->PrototypeTemplate()->Set(v8::String::NewSymbol(name),            \
                                  __callback##_TEM);                      \
} while (0)

class NodeMidiOutput : ObjectWrap
{
private:
    RtMidiOut* out;
public:
    static v8::Persistent<v8::FunctionTemplate> s_ct;
    static void Init(v8::Handle<v8::Object> target)
    {
        v8::HandleScope scope;

        v8::Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(New);

        s_ct = v8::Persistent<v8::FunctionTemplate>::New(t);
        s_ct->InstanceTemplate()->SetInternalFieldCount(1);
        s_ct->SetClassName(v8::String::NewSymbol("NodeMidiOutput"));

        SAFE_NODE_SET_PROTOTYPE_METHOD(s_ct, "getPortCount", GetPortCount);
        SAFE_NODE_SET_PROTOTYPE_METHOD(s_ct, "getPortName", GetPortName);

        SAFE_NODE_SET_PROTOTYPE_METHOD(s_ct, "openPort", OpenPort);
        SAFE_NODE_SET_PROTOTYPE_METHOD(s_ct, "openVirtualPort", OpenVirtualPort);
        SAFE_NODE_SET_PROTOTYPE_METHOD(s_ct, "closePort", ClosePort);

        SAFE_NODE_SET_PROTOTYPE_METHOD(s_ct, "sendMessage", SendMessage);

        target->Set(v8::String::NewSymbol("output"),
                    s_ct->GetFunction());
    }

    NodeMidiOutput()
    {
        out = new RtMidiOut();
    }

    ~NodeMidiOutput()
    {
        delete out;
    }

    static v8::Handle<v8::Value> New(const v8::Arguments& args)
    {
        v8::HandleScope scope;
        NodeMidiOutput* output = new NodeMidiOutput();
        output->Wrap(args.This());
        return args.This();
    }

    static v8::Handle<v8::Value> GetPortCount(const v8::Arguments& args)
    {
        v8::HandleScope scope;
        NodeMidiOutput* output = ObjectWrap::Unwrap<NodeMidiOutput>(args.This());
        v8::Local<v8::Integer> result = v8::Uint32::New(output->out->getPortCount());
        return scope.Close(result);
    }

    static v8::Handle<v8::Value> GetPortName(const v8::Arguments& args)
    {
        v8::HandleScope scope;
        NodeMidiOutput* output = ObjectWrap::Unwrap<NodeMidiOutput>(args.This());
        if (args.Length() == 0 || !args[0]->IsUint32()) {
            return ThrowException(v8::Exception::TypeError(
                v8::String::New("First argument must be an integer")));
        }
        unsigned int portNumber = args[0]->Uint32Value();
        v8::Local<v8::String> result = v8::String::New(output->out->getPortName(portNumber).c_str());
        return scope.Close(result);
    }

    static v8::Handle<v8::Value> OpenPort(const v8::Arguments& args)
    {
        v8::HandleScope scope;
        NodeMidiOutput* output = ObjectWrap::Unwrap<NodeMidiOutput>(args.This());
        if (args.Length() == 0 || !args[0]->IsUint32()) {
            return ThrowException(v8::Exception::TypeError(
                v8::String::New("First argument must be an integer")));
        }
        unsigned int portNumber = args[0]->Uint32Value();
        output->out->openPort(portNumber);
        return scope.Close(v8::Undefined());
    }

    static v8::Handle<v8::Value> OpenVirtualPort(const v8::Arguments& args)
    {
        v8::HandleScope scope;
        NodeMidiOutput* output = ObjectWrap::Unwrap<NodeMidiOutput>(args.This());
        if (args.Length() == 0 || !args[0]->IsString()) {
            return ThrowException(v8::Exception::TypeError(
                v8::String::New("First argument must be a string")));
        }
        std::string name(*v8::String::AsciiValue(args[0]));
        output->out->openVirtualPort(name);
        return scope.Close(v8::Undefined());
    }

    static v8::Handle<v8::Value> ClosePort(const v8::Arguments& args)
    {
        v8::HandleScope scope;
        NodeMidiOutput* output = ObjectWrap::Unwrap<NodeMidiOutput>(args.This());
        output->out->closePort();
        return scope.Close(v8::Undefined());
    }

    static v8::Handle<v8::Value> SendMessage(const v8::Arguments& args)
    {
        v8::HandleScope scope;
        NodeMidiOutput* output = ObjectWrap::Unwrap<NodeMidiOutput>(args.This());
        if (args.Length() == 0 || !args[0]->IsArray()) {
            return ThrowException(v8::Exception::TypeError(
                v8::String::New("First argument must be an array")));
        }
        v8::Local<v8::Object> message = args[0]->ToObject();
        size_t messageLength = message->Get(v8::String::New("length"))->Int32Value();
        std::vector<unsigned char> messageOutput;
        for (size_t i = 0; i != messageLength; ++i) {
            messageOutput.push_back(message->Get(v8::Integer::New(i))->Int32Value());
        }
        output->out->sendMessage(&messageOutput);
        return scope.Close(v8::Undefined());
    }
};

static v8::Persistent<v8::String> emit_symbol;

class NodeMidiInput : public ObjectWrap
{
private:
    RtMidiIn* in;

public:
    ev_async* message_async;
    pthread_mutex_t message_mutex;

    struct MidiMessage
    {
        double deltaTime;
        std::vector<unsigned char> message;
    };
    std::queue<MidiMessage*> message_queue;

    static v8::Persistent<v8::FunctionTemplate> s_ct;
    static void Init(v8::Handle<v8::Object> target)
    {
        v8::HandleScope scope;

        v8::Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(New);

        s_ct = v8::Persistent<v8::FunctionTemplate>::New(t);
        s_ct->InstanceTemplate()->SetInternalFieldCount(1);

        emit_symbol = NODE_PSYMBOL("emit");

        s_ct->SetClassName(v8::String::NewSymbol("NodeMidiInput"));

        SAFE_NODE_SET_PROTOTYPE_METHOD(s_ct, "getPortCount", GetPortCount);
        SAFE_NODE_SET_PROTOTYPE_METHOD(s_ct, "getPortName", GetPortName);

        SAFE_NODE_SET_PROTOTYPE_METHOD(s_ct, "openPort", OpenPort);
        SAFE_NODE_SET_PROTOTYPE_METHOD(s_ct, "openVirtualPort", OpenVirtualPort);
        SAFE_NODE_SET_PROTOTYPE_METHOD(s_ct, "closePort", ClosePort);

        SAFE_NODE_SET_PROTOTYPE_METHOD(s_ct, "ignoreTypes", IgnoreTypes);

        target->Set(v8::String::NewSymbol("input"),
                    s_ct->GetFunction());
    }

    NodeMidiInput()
    {
        in = new RtMidiIn();
        pthread_mutex_init(&message_mutex, NULL);
        message_async = new ev_async();
    }

    ~NodeMidiInput()
    {
        in->closePort();
        ev_async_stop(EV_DEFAULT_UC_ message_async);
        delete message_async;
        delete in;
        pthread_mutex_destroy(&message_mutex);
    }

    static void EmitMessage(EV_P_ ev_async *w, int revents)
    {
        v8::HandleScope scope;
        NodeMidiInput *input = static_cast<NodeMidiInput*>(w->data);
        pthread_mutex_lock(&input->message_mutex);
        while (!input->message_queue.empty())
        {
            MidiMessage* message = input->message_queue.front();
            v8::Local<v8::Value> args[3];
            args[0]= v8::String::New("message");
            args[1] = v8::Local<v8::Value>::New(v8::Number::New(message->deltaTime));
            size_t count = message->message.size();
            v8::Local<v8::Array> data = v8::Array::New(count);
            for (size_t i = 0; i < count; ++i) {
                data->Set(v8::Number::New(i), v8::Integer::New(message->message[i]));
            }
            args[2] = v8::Local<v8::Value>::New(data);
            v8::Local<v8::Value> emit_v = input->handle_->Get(emit_symbol);
            if (emit_v->IsFunction()) {
                v8::Local<v8::Function> emit=v8::Local<v8::Function>::Cast(emit_v);
                v8::TryCatch tc;
                emit->Call(input->handle_,3,args);
                if (tc.HasCaught()){
                    node::FatalException(tc);
               }
            }
            input->message_queue.pop();
            delete message;
        }
        pthread_mutex_unlock(&input->message_mutex);
    }

    static void Callback(double deltaTime, std::vector<unsigned char> *message, void *userData)
    {
        NodeMidiInput *input = static_cast<NodeMidiInput*>(userData);
        MidiMessage* data = new MidiMessage();
        data->deltaTime = deltaTime;
        data->message = *message;
        pthread_mutex_lock(&input->message_mutex);
        input->message_queue.push(data);
        pthread_mutex_unlock(&input->message_mutex);
        ev_async_send(EV_DEFAULT_UC_ input->message_async);
    }

    static v8::Handle<v8::Value> New(const v8::Arguments& args)
    {
        v8::HandleScope scope;
        NodeMidiInput* input = new NodeMidiInput();
        input->message_async->data = input;
        ev_async_init(input->message_async, NodeMidiInput::EmitMessage);
        ev_async_start(EV_DEFAULT_UC_ input->message_async);
        ev_unref(EV_DEFAULT_UC);
        input->Wrap(args.This());
        return args.This();
    }

    static v8::Handle<v8::Value> GetPortCount(const v8::Arguments& args)
    {
        v8::HandleScope scope;
        NodeMidiInput* input = ObjectWrap::Unwrap<NodeMidiInput>(args.This());
        v8::Local<v8::Integer> result = v8::Uint32::New(input->in->getPortCount());
        return scope.Close(result);
    }

    static v8::Handle<v8::Value> GetPortName(const v8::Arguments& args)
    {
        v8::HandleScope scope;
        NodeMidiInput* input = ObjectWrap::Unwrap<NodeMidiInput>(args.This());
        if (args.Length() == 0 || !args[0]->IsUint32()) {
            return ThrowException(v8::Exception::TypeError(
                v8::String::New("First argument must be an integer")));
        }
        unsigned int portNumber = args[0]->Uint32Value();
        v8::Local<v8::String> result = v8::String::New(input->in->getPortName(portNumber).c_str());
        return scope.Close(result);
    }

    static v8::Handle<v8::Value> OpenPort(const v8::Arguments& args)
    {
        v8::HandleScope scope;
        NodeMidiInput* input = ObjectWrap::Unwrap<NodeMidiInput>(args.This());
        if (args.Length() == 0 || !args[0]->IsUint32()) {
            return ThrowException(v8::Exception::TypeError(
                v8::String::New("First argument must be an integer")));
        }
        unsigned int portNumber = args[0]->Uint32Value();
        input->Ref();
        input->in->setCallback(&NodeMidiInput::Callback, ObjectWrap::Unwrap<NodeMidiInput>(args.This()));
        input->in->openPort(portNumber);
        return scope.Close(v8::Undefined());
    }

    static v8::Handle<v8::Value> OpenVirtualPort(const v8::Arguments& args)
    {
        v8::HandleScope scope;
        NodeMidiInput* input = ObjectWrap::Unwrap<NodeMidiInput>(args.This());
        if (args.Length() == 0 || !args[0]->IsString()) {
            return ThrowException(v8::Exception::TypeError(
                v8::String::New("First argument must be a string")));
        }
        std::string name(*v8::String::AsciiValue(args[0]));
        input->Ref();
        input->in->setCallback(&NodeMidiInput::Callback, ObjectWrap::Unwrap<NodeMidiInput>(args.This()));
        input->in->openVirtualPort(name);
        return scope.Close(v8::Undefined());
    }

    static v8::Handle<v8::Value> ClosePort(const v8::Arguments& args)
    {
        v8::HandleScope scope;
        NodeMidiInput* input = ObjectWrap::Unwrap<NodeMidiInput>(args.This());
        input->Unref();
        input->in->closePort();
        return scope.Close(v8::Undefined());
    }

    static v8::Handle<v8::Value> IgnoreTypes(const v8::Arguments& args)
    {
        v8::HandleScope scope;
        NodeMidiInput* input = ObjectWrap::Unwrap<NodeMidiInput>(args.This());
        if (args.Length() != 3 || !args[0]->IsBoolean() || !args[1]->IsBoolean() || !args[2]->IsBoolean()) {
            return ThrowException(v8::Exception::TypeError(
                v8::String::New("Arguments must be boolean")));
        }
        int filter_sysex = args[0]->BooleanValue();
        int filter_timing = args[1]->BooleanValue();
        int filter_sensing = args[2]->BooleanValue();
        input->in->ignoreTypes(filter_sysex,filter_timing,filter_sensing);
        return scope.Close(v8::Undefined());
    }
};

v8::Persistent<v8::FunctionTemplate> NodeMidiOutput::s_ct;
v8::Persistent<v8::FunctionTemplate> NodeMidiInput::s_ct;

extern "C" {
    void init (v8::Handle<v8::Object> target)
    {
        NodeMidiOutput::Init(target);
        NodeMidiInput::Init(target);
    }

    NODE_MODULE(nodemidi, init);
}
<|endoftext|>