<commit_before>/***********************************************************************************************************************
 **
 ** Copyright (c) 2011, 2015 ETH Zurich
 ** 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.
 **    * Neither the name of the ETH Zurich nor the names of its contributors may be used to endorse or promote products
 **      derived from this software without specific prior written permission.
 **
 **
 ** 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.
 **
 **********************************************************************************************************************/

#include "ClangHelpers.h"

#include "OOModel/src/declarations/Project.h"
#include "OOModel/src/declarations/MetaDefinition.h"

#include "CppExport/src/exporter/CppExporter.h"
#include "CppExport/src/CodeUnit.h"

namespace CppImport {

QString ClangHelpers::spelling(clang::SourceRange sourceRange) const
{
	clang::SourceLocation b = sourceManager_->getSpellingLoc(sourceRange.getBegin());
	clang::SourceLocation e = clang::Lexer::getLocForEndOfToken(sourceManager_->getSpellingLoc(sourceRange.getEnd()), 0,
																					*sourceManager_, preprocessor_->getLangOpts());

	// this can help in preventing an uncaught exception due to questionable input
	//if (sourceManager_->getPresumedLoc(b).getFilename() != sourceManager_->getPresumedLoc(e).getFilename()) return "";

	bool invalid = true;
	auto beginPtr = sourceManager_->getCharacterData(b, &invalid);
	Q_ASSERT(!invalid);

	auto endPtr = sourceManager_->getCharacterData(e, &invalid);
	Q_ASSERT(!invalid);

	auto length = endPtr - beginPtr;
	return 0 < length ? QString::fromStdString(std::string(beginPtr, endPtr - beginPtr)) : "";
}

QString ClangHelpers::unexpandedSpelling(clang::SourceRange range) const
{
	auto result = spelling(getUnexpandedRange(range));
	while (result.startsWith("\\")) result = result.right(result.length() - 1);
	return result.trimmed();
}

clang::SourceRange ClangHelpers::getUnexpandedRange(clang::SourceRange sourceRange) const
{
	auto start = sourceRange.getBegin();
	auto end = sourceRange.getEnd();

	if (// start is a macro location and...
		 start.isMacroID() &&
		 // start was expanded from a macro argument or...
		 (sourceManager_->isMacroArgExpansion(start) ||
		 // start was expanded from a "virtual" macro i.e. it is not written anywhere (identifier concatentation etc.)
		 !sourceManager_->getFileEntryForID(sourceManager_->getFileID(sourceManager_->getSpellingLoc(start)))))
		start = sourceManager_->getImmediateExpansionRange(start).first;

	if (// end is a macro location and...
		 end.isMacroID() &&
		 // end was expanded from a macro argument or...
		 (sourceManager_->isMacroArgExpansion(end) ||
		 // end was expanded from a "virtual" macro i.e. it is not written anywhere (identifier concatentation etc.)
		 !sourceManager_->getFileEntryForID(sourceManager_->getFileID(sourceManager_->getSpellingLoc(end)))))
		end = sourceManager_->getImmediateExpansionRange(end).second;

	return clang::SourceRange{start, end};
}

ClangHelpers::ClangHelpers(OOModel::Project* rootProject, QString rootProjectPath)
	: rootProject_{rootProject}, rootProjectPath_{rootProjectPath} {}

clang::SourceLocation ClangHelpers::immediateMacroLocation(clang::SourceLocation location) const
{
	// this code is an adaptation of clang::lexer::immediateMacroName

	if (location.isMacroID())
		while (true)
		{
			auto fileID = sourceManager_->getFileID(location);
			auto sourceLocationEntry = &sourceManager_->getSLocEntry(fileID);
			if (!sourceLocationEntry->isExpansion()) break;

			auto expansion = sourceLocationEntry->getExpansion();
			location = expansion.getExpansionLocStart();
			if (!expansion.isMacroArgExpansion()) break;

			location = sourceManager_->getImmediateExpansionRange(location).first;
			auto spellingLocation = expansion.getSpellingLoc();
			if (spellingLocation.isFileID())	break;

			auto macroFileID = sourceManager_->getFileID(location);
			if (sourceManager_->isInFileID(spellingLocation, macroFileID))	break;

			location = spellingLocation;
		}

	return location;
}

void ClangHelpers::immediateSpellingHistory(clang::SourceLocation location,
														  QList<clang::SourceLocation>& result) const
{
	result.append(location);

	auto next = sourceManager_->getImmediateSpellingLoc(location);

	if (next != location)
		immediateSpellingHistory(next, result);
}

QList<QString> ClangHelpers::argumentNames(const clang::MacroDirective* definition) const
{
	QList<QString> result;

	for (auto i = definition->getMacroInfo()->arg_begin(); i != definition->getMacroInfo()->arg_end(); i++)
		result.append(QString::fromStdString((*i)->getName().str()));

	return result;
}

void ClangHelpers::deleteNode(Model::Node* node)
{
	QList<Model::Node*> workList{node};
	while (!workList.empty())
	{
		auto current = workList.takeLast();
		workList << current->children();
		envisionToClangMap_.remove(current);
	}

	SAFE_DELETE(node);
}

QStringList ClangHelpers::folderNamesFromPath(QString path)
{
	QStringList result;
	QDir dir{path};
	while (dir.cdUp())
	{
		if (dir.absolutePath() == rootProjectPath_) break;
		result.append(dir.dirName());
	}
	result.removeLast();
	return result;
}

void ClangHelpers::insertDeclarationInFolder(OOModel::Declaration* declaration, clang::SourceLocation location,
															OOModel::Declaration* parentNonFolderDeclaration)
{
	auto inserter = [declaration](auto container)
	{
		if (auto castedDeclaration = DCast<OOModel::Field>(declaration))
			container->fields()->append(castedDeclaration);
		else if (auto castedDeclaration = DCast<OOModel::Class>(declaration))
			container->classes()->append(castedDeclaration);
		else if (auto castedDeclaration = DCast<OOModel::Method>(declaration))
			container->methods()->append(castedDeclaration);
		else if (auto castedDeclaration = DCast<OOModel::MetaDefinition>(declaration))
			container->subDeclarations()->append(castedDeclaration);
	};
	auto folder = folderForLocation(location, parentNonFolderDeclaration);
	if (auto container = DCast<OOModel::Project>(folder)) inserter(container);
	else if (auto container = DCast<OOModel::Module>(folder)) inserter(container);
	else Q_ASSERT(false);
}

QString ClangHelpers::presumedFilenameWithExtension(clang::SourceLocation location) const
{
	auto presumedLocation = sourceManager_->getPresumedLoc(location);
	QDir dir{presumedLocation.getFilename()};
	auto fileName = dir.absolutePath();
	return fileName;
}

QString ClangHelpers::presumedFilenameWithoutExtension(clang::SourceLocation location) const
{
	auto fileName = presumedFilenameWithExtension(location);
	return fileName.left(fileName.lastIndexOf("."));
}

OOModel::Declaration* ClangHelpers::folderForLocation(clang::SourceLocation location,
																		OOModel::Declaration* parentNonFolderDeclaration)
{
	Q_ASSERT(parentNonFolderDeclaration);
	Q_ASSERT(DCast<OOModel::Module>(parentNonFolderDeclaration) || DCast<OOModel::Project>(parentNonFolderDeclaration));

	auto currentFolder = parentNonFolderDeclaration;
	auto presumedLocation = sourceManager()->getPresumedLoc(location);
	auto folderNames = folderNamesFromPath(presumedLocation.getFilename());
	while (!folderNames.empty())
	{
		auto requestedFolderName = folderNames.takeLast();
		auto moduleFound = false;
		Model::TypedList<OOModel::Module>* modules{};
		if (auto currentProject = DCast<OOModel::Project>(currentFolder))
			modules = currentProject->modules();
		else if (auto currentModule = DCast<OOModel::Module>(currentFolder))
			modules = currentModule->modules();

		for (auto childFolder : *modules)
			if (childFolder->name() == requestedFolderName)
			{
				currentFolder = childFolder;
				moduleFound = true;
				break;
			}
		if (!moduleFound)
		{
			auto newFolder = new OOModel::Module{requestedFolderName, OOModel::Module::ModuleKind::Folder};
			if (auto currentProject = DCast<OOModel::Project>(currentFolder))
				currentProject->modules()->append(newFolder);
			else if (auto currentModule = DCast<OOModel::Module>(currentFolder))
				currentModule->modules()->append(newFolder);
			currentFolder = newFolder;
		}
	}

	Q_ASSERT(DCast<OOModel::Module>(currentFolder) || DCast<OOModel::Project>(currentFolder));
	return currentFolder;
}

OOModel::Project* ClangHelpers::projectForLocation(clang::SourceLocation location)
{
	auto presumedLocation = sourceManager()->getPresumedLoc(location);
	auto parentName = projectNameFromPath(presumedLocation.getFilename());
	if (!parentName.isEmpty())
	{
		auto project = projectByName(parentName);
		if (!project)
		{
			project = new OOModel::Project{parentName};
			rootProject_->projects()->append(project);
			projects_.insert(parentName, project);
		}
		return project;
	}
	return rootProject_;
}

QString ClangHelpers::projectNameFromPath(QString path)
{
	QRegularExpression regex{rootProjectPath_ + "/(\\w+)/"};
	auto m = regex.match(QDir{path}.absolutePath());
	if (m.hasMatch())
		return m.captured(1);
	return {};
}

void ClangHelpers::attachDeclarationComments(clang::NamedDecl* namedDecl, Model::Node* receiver) const
{
	if (auto commentForDeclaration = namedDecl->getASTContext().getRawCommentForDeclNoCache(namedDecl))
	{
		auto compositeNode = DCast<Model::CompositeNode>(receiver);
		Q_ASSERT(compositeNode);

		for (Comment* clangComment : comments_)
			if (clangComment->rawComment() == commentForDeclaration && !clangComment->node())
			{
				clangComment->setNode(compositeNode);

				if (compositeNode->comment())
				{
					// This code is here for future reference, but we don't actually support this case at the moment.
					// It would be necessary to check if the lines we are about to attach already exist.
					return;

					auto commentNode = DCast<Comments::CommentNode>(compositeNode->comment());
					Q_ASSERT(commentNode);

					commentNode->appendText(clangComment->text());
				}
				else compositeNode->setComment(new Comments::CommentNode{clangComment->text()});
			}
	}
}

void ClangHelpers::printMacroDefinitionForDebug(const clang::MacroDirective* macroDirective) const
{
	qDebug() << "Macro in: " << presumedFilenameWithExtension(macroDirective->getMacroInfo()->getDefinitionLoc());
	qDebug() << spelling(clang::SourceRange{macroDirective->getMacroInfo()->getDefinitionLoc(),
																  macroDirective->getMacroInfo()->getDefinitionEndLoc()});
}

void ClangHelpers::associateNodeWithPresumedFileLocation(Model::Node* node, clang::SourceLocation location)
{
	auto fullPath = presumedFilenameWithoutExtension(location);
	nodeToFilenameMap_ [node] = fullPath.right(fullPath.size() - rootProjectPath_.size() - 1);
}

void ClangHelpers::associateNodeWithPresumedFileLocation(Model::Node* node, Model::Node* from)
{
	auto it = nodeToFilenameMap_.find(from);
	Q_ASSERT(it != nodeToFilenameMap_.end());
	nodeToFilenameMap_[node] = *it;
}

void ClangHelpers::exportMergeMapToJson(QString filename)
{
	QHash<QString, QString> exportedMap;
	QList<CppExport::CodeUnit*> codeUnits;
	CppExport::CppExporter::units(rootProject_, codeUnits, exportedMap);

	QHash<QString, QString> generatedExportedMap;
	for (auto unit : codeUnits)
	{
		auto nodeFilename = nodeToFilenameMap_[unit->node()];

		if (generatedExportedMap.contains(unit->name()))
		{
			if (generatedExportedMap.value(unit->name()) != nodeFilename)
			{
				qDebug() << unit->name() << "mapped at least twice with different values:";
				qDebug() << "1:   " << nodeFilename;
				qDebug() << "2:   " << generatedExportedMap.value(unit->name());
				Q_ASSERT(false);
			}
		}
		else
			generatedExportedMap.insert(unit->name(), nodeFilename);
	}

	QFile file{filename};
	file.open(QIODevice::WriteOnly | QIODevice::Text);
	QTextStream out{&file};
	out << "{\n";
	out << "\t\"DependencyUnitMergeMap\" :\n";
	out << "\t{\n";
	for (auto it = generatedExportedMap.begin(); it != generatedExportedMap.end(); it++)
		if (it.key() != it.value())
			out << "\t\t\"" << it.key() << "\" : \"" << it.value() << "\",\n";
	out << "\t},\n";
	out << "\t\"SeparateTemplateImplementationSet\" : [ ]\n";
	out << "}";
	file.close();
}

}
<commit_msg>make generated exported map minimal<commit_after>/***********************************************************************************************************************
 **
 ** Copyright (c) 2011, 2015 ETH Zurich
 ** 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.
 **    * Neither the name of the ETH Zurich nor the names of its contributors may be used to endorse or promote products
 **      derived from this software without specific prior written permission.
 **
 **
 ** 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.
 **
 **********************************************************************************************************************/

#include "ClangHelpers.h"

#include "OOModel/src/declarations/Project.h"
#include "OOModel/src/declarations/MetaDefinition.h"

#include "CppExport/src/exporter/CppExporter.h"
#include "CppExport/src/CodeUnit.h"

namespace CppImport {

QString ClangHelpers::spelling(clang::SourceRange sourceRange) const
{
	clang::SourceLocation b = sourceManager_->getSpellingLoc(sourceRange.getBegin());
	clang::SourceLocation e = clang::Lexer::getLocForEndOfToken(sourceManager_->getSpellingLoc(sourceRange.getEnd()), 0,
																					*sourceManager_, preprocessor_->getLangOpts());

	// this can help in preventing an uncaught exception due to questionable input
	//if (sourceManager_->getPresumedLoc(b).getFilename() != sourceManager_->getPresumedLoc(e).getFilename()) return "";

	bool invalid = true;
	auto beginPtr = sourceManager_->getCharacterData(b, &invalid);
	Q_ASSERT(!invalid);

	auto endPtr = sourceManager_->getCharacterData(e, &invalid);
	Q_ASSERT(!invalid);

	auto length = endPtr - beginPtr;
	return 0 < length ? QString::fromStdString(std::string(beginPtr, endPtr - beginPtr)) : "";
}

QString ClangHelpers::unexpandedSpelling(clang::SourceRange range) const
{
	auto result = spelling(getUnexpandedRange(range));
	while (result.startsWith("\\")) result = result.right(result.length() - 1);
	return result.trimmed();
}

clang::SourceRange ClangHelpers::getUnexpandedRange(clang::SourceRange sourceRange) const
{
	auto start = sourceRange.getBegin();
	auto end = sourceRange.getEnd();

	if (// start is a macro location and...
		 start.isMacroID() &&
		 // start was expanded from a macro argument or...
		 (sourceManager_->isMacroArgExpansion(start) ||
		 // start was expanded from a "virtual" macro i.e. it is not written anywhere (identifier concatentation etc.)
		 !sourceManager_->getFileEntryForID(sourceManager_->getFileID(sourceManager_->getSpellingLoc(start)))))
		start = sourceManager_->getImmediateExpansionRange(start).first;

	if (// end is a macro location and...
		 end.isMacroID() &&
		 // end was expanded from a macro argument or...
		 (sourceManager_->isMacroArgExpansion(end) ||
		 // end was expanded from a "virtual" macro i.e. it is not written anywhere (identifier concatentation etc.)
		 !sourceManager_->getFileEntryForID(sourceManager_->getFileID(sourceManager_->getSpellingLoc(end)))))
		end = sourceManager_->getImmediateExpansionRange(end).second;

	return clang::SourceRange{start, end};
}

ClangHelpers::ClangHelpers(OOModel::Project* rootProject, QString rootProjectPath)
	: rootProject_{rootProject}, rootProjectPath_{rootProjectPath} {}

clang::SourceLocation ClangHelpers::immediateMacroLocation(clang::SourceLocation location) const
{
	// this code is an adaptation of clang::lexer::immediateMacroName

	if (location.isMacroID())
		while (true)
		{
			auto fileID = sourceManager_->getFileID(location);
			auto sourceLocationEntry = &sourceManager_->getSLocEntry(fileID);
			if (!sourceLocationEntry->isExpansion()) break;

			auto expansion = sourceLocationEntry->getExpansion();
			location = expansion.getExpansionLocStart();
			if (!expansion.isMacroArgExpansion()) break;

			location = sourceManager_->getImmediateExpansionRange(location).first;
			auto spellingLocation = expansion.getSpellingLoc();
			if (spellingLocation.isFileID())	break;

			auto macroFileID = sourceManager_->getFileID(location);
			if (sourceManager_->isInFileID(spellingLocation, macroFileID))	break;

			location = spellingLocation;
		}

	return location;
}

void ClangHelpers::immediateSpellingHistory(clang::SourceLocation location,
														  QList<clang::SourceLocation>& result) const
{
	result.append(location);

	auto next = sourceManager_->getImmediateSpellingLoc(location);

	if (next != location)
		immediateSpellingHistory(next, result);
}

QList<QString> ClangHelpers::argumentNames(const clang::MacroDirective* definition) const
{
	QList<QString> result;

	for (auto i = definition->getMacroInfo()->arg_begin(); i != definition->getMacroInfo()->arg_end(); i++)
		result.append(QString::fromStdString((*i)->getName().str()));

	return result;
}

void ClangHelpers::deleteNode(Model::Node* node)
{
	QList<Model::Node*> workList{node};
	while (!workList.empty())
	{
		auto current = workList.takeLast();
		workList << current->children();
		envisionToClangMap_.remove(current);
	}

	SAFE_DELETE(node);
}

QStringList ClangHelpers::folderNamesFromPath(QString path)
{
	QStringList result;
	QDir dir{path};
	while (dir.cdUp())
	{
		if (dir.absolutePath() == rootProjectPath_) break;
		result.append(dir.dirName());
	}
	result.removeLast();
	return result;
}

void ClangHelpers::insertDeclarationInFolder(OOModel::Declaration* declaration, clang::SourceLocation location,
															OOModel::Declaration* parentNonFolderDeclaration)
{
	auto inserter = [declaration](auto container)
	{
		if (auto castedDeclaration = DCast<OOModel::Field>(declaration))
			container->fields()->append(castedDeclaration);
		else if (auto castedDeclaration = DCast<OOModel::Class>(declaration))
			container->classes()->append(castedDeclaration);
		else if (auto castedDeclaration = DCast<OOModel::Method>(declaration))
			container->methods()->append(castedDeclaration);
		else if (auto castedDeclaration = DCast<OOModel::MetaDefinition>(declaration))
			container->subDeclarations()->append(castedDeclaration);
	};
	auto folder = folderForLocation(location, parentNonFolderDeclaration);
	if (auto container = DCast<OOModel::Project>(folder)) inserter(container);
	else if (auto container = DCast<OOModel::Module>(folder)) inserter(container);
	else Q_ASSERT(false);
}

QString ClangHelpers::presumedFilenameWithExtension(clang::SourceLocation location) const
{
	auto presumedLocation = sourceManager_->getPresumedLoc(location);
	QDir dir{presumedLocation.getFilename()};
	auto fileName = dir.absolutePath();
	return fileName;
}

QString ClangHelpers::presumedFilenameWithoutExtension(clang::SourceLocation location) const
{
	auto fileName = presumedFilenameWithExtension(location);
	return fileName.left(fileName.lastIndexOf("."));
}

OOModel::Declaration* ClangHelpers::folderForLocation(clang::SourceLocation location,
																		OOModel::Declaration* parentNonFolderDeclaration)
{
	Q_ASSERT(parentNonFolderDeclaration);
	Q_ASSERT(DCast<OOModel::Module>(parentNonFolderDeclaration) || DCast<OOModel::Project>(parentNonFolderDeclaration));

	auto currentFolder = parentNonFolderDeclaration;
	auto presumedLocation = sourceManager()->getPresumedLoc(location);
	auto folderNames = folderNamesFromPath(presumedLocation.getFilename());
	while (!folderNames.empty())
	{
		auto requestedFolderName = folderNames.takeLast();
		auto moduleFound = false;
		Model::TypedList<OOModel::Module>* modules{};
		if (auto currentProject = DCast<OOModel::Project>(currentFolder))
			modules = currentProject->modules();
		else if (auto currentModule = DCast<OOModel::Module>(currentFolder))
			modules = currentModule->modules();

		for (auto childFolder : *modules)
			if (childFolder->name() == requestedFolderName)
			{
				currentFolder = childFolder;
				moduleFound = true;
				break;
			}
		if (!moduleFound)
		{
			auto newFolder = new OOModel::Module{requestedFolderName, OOModel::Module::ModuleKind::Folder};
			if (auto currentProject = DCast<OOModel::Project>(currentFolder))
				currentProject->modules()->append(newFolder);
			else if (auto currentModule = DCast<OOModel::Module>(currentFolder))
				currentModule->modules()->append(newFolder);
			currentFolder = newFolder;
		}
	}

	Q_ASSERT(DCast<OOModel::Module>(currentFolder) || DCast<OOModel::Project>(currentFolder));
	return currentFolder;
}

OOModel::Project* ClangHelpers::projectForLocation(clang::SourceLocation location)
{
	auto presumedLocation = sourceManager()->getPresumedLoc(location);
	auto parentName = projectNameFromPath(presumedLocation.getFilename());
	if (!parentName.isEmpty())
	{
		auto project = projectByName(parentName);
		if (!project)
		{
			project = new OOModel::Project{parentName};
			rootProject_->projects()->append(project);
			projects_.insert(parentName, project);
		}
		return project;
	}
	return rootProject_;
}

QString ClangHelpers::projectNameFromPath(QString path)
{
	QRegularExpression regex{rootProjectPath_ + "/(\\w+)/"};
	auto m = regex.match(QDir{path}.absolutePath());
	if (m.hasMatch())
		return m.captured(1);
	return {};
}

void ClangHelpers::attachDeclarationComments(clang::NamedDecl* namedDecl, Model::Node* receiver) const
{
	if (auto commentForDeclaration = namedDecl->getASTContext().getRawCommentForDeclNoCache(namedDecl))
	{
		auto compositeNode = DCast<Model::CompositeNode>(receiver);
		Q_ASSERT(compositeNode);

		for (Comment* clangComment : comments_)
			if (clangComment->rawComment() == commentForDeclaration && !clangComment->node())
			{
				clangComment->setNode(compositeNode);

				if (compositeNode->comment())
				{
					// This code is here for future reference, but we don't actually support this case at the moment.
					// It would be necessary to check if the lines we are about to attach already exist.
					return;

					auto commentNode = DCast<Comments::CommentNode>(compositeNode->comment());
					Q_ASSERT(commentNode);

					commentNode->appendText(clangComment->text());
				}
				else compositeNode->setComment(new Comments::CommentNode{clangComment->text()});
			}
	}
}

void ClangHelpers::printMacroDefinitionForDebug(const clang::MacroDirective* macroDirective) const
{
	qDebug() << "Macro in: " << presumedFilenameWithExtension(macroDirective->getMacroInfo()->getDefinitionLoc());
	qDebug() << spelling(clang::SourceRange{macroDirective->getMacroInfo()->getDefinitionLoc(),
																  macroDirective->getMacroInfo()->getDefinitionEndLoc()});
}

void ClangHelpers::associateNodeWithPresumedFileLocation(Model::Node* node, clang::SourceLocation location)
{
	auto fullPath = presumedFilenameWithoutExtension(location);
	nodeToFilenameMap_ [node] = fullPath.right(fullPath.size() - rootProjectPath_.size() - 1);
}

void ClangHelpers::associateNodeWithPresumedFileLocation(Model::Node* node, Model::Node* from)
{
	auto it = nodeToFilenameMap_.find(from);
	Q_ASSERT(it != nodeToFilenameMap_.end());
	nodeToFilenameMap_[node] = *it;
}

void ClangHelpers::exportMergeMapToJson(QString filename)
{
	QHash<QString, QString> exportedMap;
	QList<CppExport::CodeUnit*> codeUnits;
	CppExport::CppExporter::units(rootProject_, codeUnits, exportedMap);

	QHash<QString, QString> generatedExportedMap;
	for (auto unit : codeUnits)
	{
		auto nodeFilename = nodeToFilenameMap_[unit->node()];

		if (generatedExportedMap.contains(unit->name()))
		{
			if (generatedExportedMap.value(unit->name()) != nodeFilename)
			{
				qDebug() << unit->name() << "mapped at least twice with different values:";
				qDebug() << "1:   " << nodeFilename;
				qDebug() << "2:   " << generatedExportedMap.value(unit->name());
				Q_ASSERT(false);
			}
		}
		else if (unit->name() != nodeFilename)
			generatedExportedMap.insert(unit->name(), nodeFilename);
	}

	QFile file{filename};
	file.open(QIODevice::WriteOnly | QIODevice::Text);
	QTextStream out{&file};
	out << "{\n";
	out << "\t\"DependencyUnitMergeMap\" :\n";
	out << "\t{\n";
	for (auto it = generatedExportedMap.begin(); it != generatedExportedMap.end(); it++)
		if (it.key() != it.value())
			out << "\t\t\"" << it.key() << "\" : \"" << it.value() << "\",\n";
	out << "\t},\n";
	out << "\t\"SeparateTemplateImplementationSet\" : [ ]\n";
	out << "}";
	file.close();
}

}
<|endoftext|>


<commit_before><commit_msg>[ntuple] Fix-up buffered sink unit test<commit_after><|endoftext|>


<commit_before>#include <st.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>

#include <linux/version.h>

// @see https://www.kernel.org/doc/html/latest/networking/msg_zerocopy.html#notification-reception
#include <sys/epoll.h>

// @see https://github.com/torvalds/linux/blob/master/tools/testing/selftests/net/msg_zerocopy.c
#include <linux/errqueue.h>
#ifndef SO_EE_ORIGIN_ZEROCOPY
#define SO_EE_ORIGIN_ZEROCOPY		5
#endif

#ifndef SO_ZEROCOPY
#define SO_ZEROCOPY	60
#endif

#ifndef SO_EE_CODE_ZEROCOPY_COPIED
#define SO_EE_CODE_ZEROCOPY_COPIED	1
#endif

#ifndef MSG_ZEROCOPY
#define MSG_ZEROCOPY	0x4000000
#endif

void* receiver(void* arg)
{
    st_netfd_t stfd = (st_netfd_t)arg;

    sockaddr_in peer;
    memset(&peer, 0, sizeof(sockaddr_in));

    char buf[1500];
    memset(buf, 0, sizeof(buf));

    iovec iov;
    iov.iov_base = buf;
    iov.iov_len = sizeof(buf);

    msghdr msg;
    memset(&msg, 0, sizeof(msghdr));
    msg.msg_name = (sockaddr_in*)&peer;
    msg.msg_namelen = sizeof(sockaddr_in);
    msg.msg_iov = &iov;
    msg.msg_iovlen = 1;

    int r0 = st_recvmsg(stfd, &msg, 0, ST_UTIME_NO_TIMEOUT);
    assert(r0 > 0);
    printf("Pong %s:%d %d bytes, flags %#x, %s\n", inet_ntoa(peer.sin_addr), ntohs(peer.sin_port), r0,
        msg.msg_flags, msg.msg_iov->iov_base);

    return NULL;
}

int main(int argc, char** argv)
{
    if (argc < 5) {
        printf("Usage: %s <host> <port> <pong> <zerocopy>\n", argv[0]);
        printf("For example:\n");
        printf("        %s 127.0.0.1 8000 true true\n", argv[0]);
        exit(-1);
    }

    char* host = argv[1];
    int port = atoi(argv[2]);
    bool pong = !strcmp(argv[3], "true");
    bool zerocopy = !strcmp(argv[4], "true");
    printf("Server listen %s:%d, pong %d, zerocopy %d\n", host, port, pong, zerocopy);

    assert(!st_set_eventsys(ST_EVENTSYS_ALT));
    assert(!st_init());

    int fd = socket(PF_INET, SOCK_DGRAM, 0);
    assert(fd > 0);

    // @see https://github.com/torvalds/linux/blob/master/tools/testing/selftests/net/msg_zerocopy.c
    if (zerocopy) {
        int one = 1;
        int r0 = setsockopt(fd, SOL_SOCKET, SO_ZEROCOPY, &one, sizeof(one));
// MSG_ZEROCOPY for UDP was added in commit b5947e5d1e71 ("udp: msg_zerocopy") in Linux 5.0.
// @see https://lore.kernel.org/netdev/CA+FuTSfBFqRViKfG5crEv8xLMgAkp3cZ+yeuELK5TVv61xT=Yw@mail.gmail.com/
#if LINUX_VERSION_CODE < KERNEL_VERSION(5,0,0)
        if (r0 == -1) {
            printf("MSG_ZEROCOPY should be kernel 5.0+, kernel %#x, errno=%d\n", LINUX_VERSION_CODE, 524);
            exit(-1);
        }
#endif
        assert(!r0);

        printf("epoll events EPOLLERR=%#x, EPOLLHUP=%#x\n", EPOLLERR, EPOLLHUP);
    }

    st_netfd_t stfd = st_netfd_open_socket(fd);
    assert(stfd);
    printf("Client fd=%d\n", fd);

    if (pong) {
        st_thread_t r0 = st_thread_create(receiver, stfd, 0, 0);
        assert(r0);
    }

    sockaddr_in peer;
    memset(&peer, 0, sizeof(sockaddr_in));

    peer.sin_family = AF_INET;
    peer.sin_port = htons(port);
    peer.sin_addr.s_addr = inet_addr(host);

    char buf[1500];
    memset(buf, 0, sizeof(buf));
    memcpy(buf, "Hello", 5);

    iovec iov;
    iov.iov_base = buf;
    iov.iov_len = strlen(buf);

    msghdr msg;
    memset(&msg, 0, sizeof(msghdr));
    msg.msg_name = (sockaddr_in*)&peer;
    msg.msg_namelen = sizeof(sockaddr_in);
    msg.msg_iov = &iov;
    msg.msg_iovlen = 1;

    int r0;
    if (zerocopy) {
        r0 = st_sendmsg(stfd, &msg, MSG_ZEROCOPY, ST_UTIME_NO_TIMEOUT);
    } else {
        r0 = st_sendmsg(stfd, &msg, 0, ST_UTIME_NO_TIMEOUT);
    }
    assert(r0 > 0);
    printf("Ping %s:%d %d bytes, r0=%d, %s\n", host, port, iov.iov_len, r0, msg.msg_iov->iov_base);

    // Reception from kernel, @see https://www.kernel.org/doc/html/latest/networking/msg_zerocopy.html#notification-reception
    // See do_recv_completion at https://github.com/torvalds/linux/blob/master/tools/testing/selftests/net/msg_zerocopy.c#L393
    char control[100];
    msg.msg_control = control;
    msg.msg_controllen = sizeof(control);
    // Note that the r0 is 0, the reception is in the control.
    r0 = st_recvmsg(stfd, &msg, MSG_ERRQUEUE, ST_UTIME_NO_TIMEOUT);
    assert(r0 >= 0);
    assert(msg.msg_flags == MSG_ERRQUEUE);

    // Notification parsing, @see https://www.kernel.org/doc/html/latest/networking/msg_zerocopy.html#notification-parsing
    cmsghdr* cm = CMSG_FIRSTHDR(&msg);
    assert(cm->cmsg_level == SOL_IP || cm->cmsg_type == IP_RECVERR);

    sock_extended_err* serr = (sock_extended_err*)(void*)CMSG_DATA(cm);
    assert(serr->ee_errno == 0 && serr->ee_origin == SO_EE_ORIGIN_ZEROCOPY);

    uint32_t hi = serr->ee_data;
    uint32_t lo = serr->ee_info;
    uint32_t range = hi - lo + 1;
    printf("Reception %d bytes, flags %#x, cmsg(level %#x, type %#x), serr(errno %#x, origin %#x, code %#x), %d [%d, %d]\n",
        msg.msg_controllen, msg.msg_flags, cm->cmsg_level, cm->cmsg_type, serr->ee_errno, serr->ee_origin, serr->ee_code, range, lo, hi);

    // Defered Copies, @see https://www.kernel.org/doc/html/latest/networking/msg_zerocopy.html#deferred-copies
    if (serr->ee_code == SO_EE_CODE_ZEROCOPY_COPIED) {
        printf("Warning: Defered copies, should stop zerocopy\n");
    }

    st_sleep(-1);

    return 0;
}
<commit_msg>Refine research zerocopy, use batch for reception<commit_after>#include <st.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>

#include <linux/version.h>

// @see https://www.kernel.org/doc/html/latest/networking/msg_zerocopy.html#notification-reception
#include <sys/epoll.h>

// @see https://github.com/torvalds/linux/blob/master/tools/testing/selftests/net/msg_zerocopy.c
#include <linux/errqueue.h>
#ifndef SO_EE_ORIGIN_ZEROCOPY
#define SO_EE_ORIGIN_ZEROCOPY		5
#endif

#ifndef SO_ZEROCOPY
#define SO_ZEROCOPY	60
#endif

#ifndef SO_EE_CODE_ZEROCOPY_COPIED
#define SO_EE_CODE_ZEROCOPY_COPIED	1
#endif

#ifndef MSG_ZEROCOPY
#define MSG_ZEROCOPY	0x4000000
#endif

void* receiver(void* arg)
{
    st_netfd_t stfd = (st_netfd_t)arg;

    for (;;) {
        sockaddr_in peer;
        memset(&peer, 0, sizeof(sockaddr_in));

        char buf[1500];
        memset(buf, 0, sizeof(buf));

        iovec iov;
        iov.iov_base = buf;
        iov.iov_len = sizeof(buf);

        msghdr msg;
        memset(&msg, 0, sizeof(msghdr));
        msg.msg_name = (sockaddr_in*)&peer;
        msg.msg_namelen = sizeof(sockaddr_in);
        msg.msg_iov = &iov;
        msg.msg_iovlen = 1;

        int r0 = st_recvmsg(stfd, &msg, 0, ST_UTIME_NO_TIMEOUT);
        assert(r0 > 0);
        printf("Pong %s:%d %d bytes, flags %#x, %s\n", inet_ntoa(peer.sin_addr), ntohs(peer.sin_port), r0,
            msg.msg_flags, msg.msg_iov->iov_base);
    }

    return NULL;
}

void parse_reception(st_netfd_t stfd)
{
    msghdr msg;
    memset(&msg, 0, sizeof(msghdr));

    // Reception from kernel, @see https://www.kernel.org/doc/html/latest/networking/msg_zerocopy.html#notification-reception
    // See do_recv_completion at https://github.com/torvalds/linux/blob/master/tools/testing/selftests/net/msg_zerocopy.c#L393
    char control[100];
    msg.msg_control = control;
    msg.msg_controllen = sizeof(control);
    // Note that the r0 is 0, the reception is in the control.
    int r0 = st_recvmsg(stfd, &msg, MSG_ERRQUEUE, ST_UTIME_NO_TIMEOUT);
    assert(r0 >= 0);
    assert(msg.msg_flags == MSG_ERRQUEUE);

    // Notification parsing, @see https://www.kernel.org/doc/html/latest/networking/msg_zerocopy.html#notification-parsing
    cmsghdr* cm = CMSG_FIRSTHDR(&msg);
    assert(cm->cmsg_level == SOL_IP || cm->cmsg_type == IP_RECVERR);

    sock_extended_err* serr = (sock_extended_err*)(void*)CMSG_DATA(cm);
    assert(serr->ee_errno == 0 && serr->ee_origin == SO_EE_ORIGIN_ZEROCOPY);

    uint32_t hi = serr->ee_data;
    uint32_t lo = serr->ee_info;
    uint32_t range = hi - lo + 1;
    printf("Reception %d bytes, flags %#x, cmsg(level %#x, type %#x), serr(errno %#x, origin %#x, code %#x), range %d [%d, %d]\n",
        msg.msg_controllen, msg.msg_flags, cm->cmsg_level, cm->cmsg_type, serr->ee_errno, serr->ee_origin, serr->ee_code, range, lo, hi);

    // Defered Copies, @see https://www.kernel.org/doc/html/latest/networking/msg_zerocopy.html#deferred-copies
    if (serr->ee_code == SO_EE_CODE_ZEROCOPY_COPIED) {
        printf("Warning: Defered copies, should stop zerocopy\n");
    }
}

int main(int argc, char** argv)
{
    if (argc < 8) {
        printf("Usage: %s <host> <port> <pong> <zerocopy> <sendmmsg> <loop> <batch>\n", argv[0]);
        printf("    pong        Whether response pong, true|false\n");
        printf("    zerocopy    Whether use zerocopy to sendmsg, true|false\n");
        printf("    sendmmsg    The copies of message, 1 means sendmmsg(msg+msg)\n");
        printf("    loop        The number of loop to send out messages\n");
        printf("    batch       Whether read reception by batch, true|false\n");
        printf("For example:\n");
        printf("        %s 127.0.0.1 8000 true true 0 1 true\n", argv[0]);
        exit(-1);
    }

    char* host = argv[1];
    int port = atoi(argv[2]);
    bool pong = !strcmp(argv[3], "true");
    bool zerocopy = !strcmp(argv[4], "true");
    int nn_copies = atoi(argv[5]);
    int loop = atoi(argv[6]);
    bool batch = !strcmp(argv[7], "true");
    printf("Server listen %s:%d, pong %d, zerocopy %d, copies %d, loop %d, batch %d\n",
        host, port, pong, zerocopy, nn_copies, loop, batch);

    assert(!st_set_eventsys(ST_EVENTSYS_ALT));
    assert(!st_init());

    int fd = socket(PF_INET, SOCK_DGRAM, 0);
    assert(fd > 0);

    // @see https://github.com/torvalds/linux/blob/master/tools/testing/selftests/net/msg_zerocopy.c
    if (zerocopy) {
        int one = 1;
        int r0 = setsockopt(fd, SOL_SOCKET, SO_ZEROCOPY, &one, sizeof(one));
// MSG_ZEROCOPY for UDP was added in commit b5947e5d1e71 ("udp: msg_zerocopy") in Linux 5.0.
// @see https://lore.kernel.org/netdev/CA+FuTSfBFqRViKfG5crEv8xLMgAkp3cZ+yeuELK5TVv61xT=Yw@mail.gmail.com/
#if LINUX_VERSION_CODE < KERNEL_VERSION(5,0,0)
        if (r0 == -1) {
            printf("MSG_ZEROCOPY should be kernel 5.0+, kernel %#x, errno=%d\n", LINUX_VERSION_CODE, 524);
            exit(-1);
        }
#endif
        assert(!r0);

        printf("epoll events EPOLLERR=%#x, EPOLLHUP=%#x\n", EPOLLERR, EPOLLHUP);
    }

    st_netfd_t stfd = st_netfd_open_socket(fd);
    assert(stfd);
    printf("Client fd=%d\n", fd);

    if (pong) {
        st_thread_t r0 = st_thread_create(receiver, stfd, 0, 0);
        assert(r0);
    }

    sockaddr_in peer;
    memset(&peer, 0, sizeof(sockaddr_in));

    peer.sin_family = AF_INET;
    peer.sin_port = htons(port);
    peer.sin_addr.s_addr = inet_addr(host);

    char buf[1500];
    memset(buf, 0, sizeof(buf));
    memcpy(buf, "Hello", 5);

    iovec iov;
    iov.iov_base = buf;
    iov.iov_len = strlen(buf);

    for (int k = 0; k < loop; k++) {
        msghdr msg;
        memset(&msg, 0, sizeof(msghdr));
        msg.msg_name = (sockaddr_in*)&peer;
        msg.msg_namelen = sizeof(sockaddr_in);
        msg.msg_iov = &iov;
        msg.msg_iovlen = 1;

        int r0;
        if (nn_copies == 0) {
            if (zerocopy) {
                r0 = st_sendmsg(stfd, &msg, MSG_ZEROCOPY, ST_UTIME_NO_TIMEOUT);
            } else {
                r0 = st_sendmsg(stfd, &msg, 0, ST_UTIME_NO_TIMEOUT);
            }
        } else {
            mmsghdr* hdrs = new mmsghdr[nn_copies + 1];
            for (int i = 0; i < nn_copies + 1; i++) {
                mmsghdr* p = hdrs + i;
                memcpy(&p->msg_hdr, &msg, sizeof(msghdr));
                p->msg_len = 0;
            }
            if (zerocopy) {
                r0 = st_sendmmsg(stfd, hdrs, nn_copies + 1, MSG_ZEROCOPY, ST_UTIME_NO_TIMEOUT);
            } else {
                r0 = st_sendmmsg(stfd, hdrs, nn_copies + 1, 0, ST_UTIME_NO_TIMEOUT);
            }
        }
        assert(r0 > 0);
        printf("Ping %s:%d %d bytes, copies=%d, r0=%d, %s\n", host, port, iov.iov_len, nn_copies, r0, msg.msg_iov->iov_base);

        if (!zerocopy) {
            continue;
        }

        if (!batch) {
            parse_reception(stfd);
        }
    }

    // @see https://www.kernel.org/doc/html/latest/networking/msg_zerocopy.html#notification-batching
    if (batch) {
        st_usleep(100 * 1000);
        parse_reception(stfd);
    }

    st_sleep(-1);
    return 0;
}
<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: ftploaderthread.hxx,v $
 *
 *  $Revision: 1.8 $
 *
 *  last change: $Author: hr $ $Date: 2006-06-20 05:25:13 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    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, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

/**************************************************************************
                                TODO
 **************************************************************************

 *************************************************************************/

#ifndef _FTP_FTPLOADERTHREAD_HXX_
#define _FTP_FTPLOADERTHREAD_HXX_

#ifndef _OSL_THREAD_H_
#include <osl/thread.h>
#endif
#include "curl.hxx"

namespace ftp {

    /** A loaderthread acts as factory for CURL-handles,
     *  the key being ( implicit ) the threadid.
     *  Owner is a FTPContentProvider-instance
     */

    class FTPLoaderThread
    {
    public:

        FTPLoaderThread();
        ~FTPLoaderThread();

        CURL* handle();

    private:

        /** Don't enable assignment and copy construction.
         *  Not defined:
         */

        FTPLoaderThread(const FTPLoaderThread&);
        FTPLoaderThread& operator=(const FTPLoaderThread&);

        oslThreadKey m_threadKey;

    };  // end class FTPLoaderThread

}

#endif
<commit_msg>INTEGRATION: CWS changefileheader (1.8.138); FILE MERGED 2008/04/01 16:02:18 thb 1.8.138.2: #i85898# Stripping all external header guards 2008/03/31 15:30:20 rt 1.8.138.1: #i87441# Change license header to LPGL v3.<commit_after>/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * Copyright 2008 by Sun Microsystems, Inc.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * $RCSfile: ftploaderthread.hxx,v $
 * $Revision: 1.9 $
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org 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 version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/

/**************************************************************************
                                TODO
 **************************************************************************

 *************************************************************************/

#ifndef _FTP_FTPLOADERTHREAD_HXX_
#define _FTP_FTPLOADERTHREAD_HXX_

#include <osl/thread.h>
#include "curl.hxx"

namespace ftp {

    /** A loaderthread acts as factory for CURL-handles,
     *  the key being ( implicit ) the threadid.
     *  Owner is a FTPContentProvider-instance
     */

    class FTPLoaderThread
    {
    public:

        FTPLoaderThread();
        ~FTPLoaderThread();

        CURL* handle();

    private:

        /** Don't enable assignment and copy construction.
         *  Not defined:
         */

        FTPLoaderThread(const FTPLoaderThread&);
        FTPLoaderThread& operator=(const FTPLoaderThread&);

        oslThreadKey m_threadKey;

    };  // end class FTPLoaderThread

}

#endif
<|endoftext|>


<commit_before>/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * Copyright 2000, 2010 Oracle and/or its affiliates.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org 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 version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/

// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_ucb.hxx"

#include <string.h>
#include <ne_xml.h>
#include "LockSequence.hxx"

using namespace webdav_ucp;
using namespace com::sun::star;

//////////////////////////////////////////////////////////////////////////

struct LockSequenceParseContext
{
    ucb::Lock * pLock;
    bool hasLockScope;
    bool hasLockType;
    bool hasDepth;
    bool hasHREF;
    bool hasTimeout;

    LockSequenceParseContext()
    : pLock( 0 ), hasLockScope( false ), hasLockType( false ),
      hasDepth( false ), hasHREF( false ), hasTimeout( false ) {}

    ~LockSequenceParseContext() { delete pLock; }
};

#define STATE_TOP (1)

#define STATE_ACTIVELOCK    (STATE_TOP)
#define STATE_LOCKSCOPE     (STATE_TOP + 1)
#define STATE_LOCKTYPE      (STATE_TOP + 2)
#define STATE_DEPTH         (STATE_TOP + 3)
#define STATE_OWNER         (STATE_TOP + 4)
#define STATE_TIMEOUT       (STATE_TOP + 5)
#define STATE_LOCKTOKEN     (STATE_TOP + 6)
#define STATE_EXCLUSIVE     (STATE_TOP + 7)
#define STATE_SHARED        (STATE_TOP + 8)
#define STATE_WRITE         (STATE_TOP + 9)
#define STATE_HREF          (STATE_TOP + 10)

//////////////////////////////////////////////////////////////////////////
extern "C" int LockSequence_startelement_callback(
    void *,
    int parent,
    const char * /*nspace*/,
    const char *name,
    const char ** )
{
    if ( name != 0 )
    {
        switch ( parent )
        {
            case NE_XML_STATEROOT:
                if ( strcmp( name, "activelock" ) == 0 )
                    return STATE_ACTIVELOCK;
                break;

            case STATE_ACTIVELOCK:
                if ( strcmp( name, "lockscope" ) == 0 )
                    return STATE_LOCKSCOPE;
                else if ( strcmp( name, "locktype" ) == 0 )
                    return STATE_LOCKTYPE;
                else if ( strcmp( name, "depth" ) == 0 )
                    return STATE_DEPTH;
                else if ( strcmp( name, "owner" ) == 0 )
                    return STATE_OWNER;
                else if ( strcmp( name, "timeout" ) == 0 )
                    return STATE_TIMEOUT;
                else if ( strcmp( name, "locktoken" ) == 0 )
                    return STATE_LOCKTOKEN;
                break;

            case STATE_LOCKSCOPE:
                if ( strcmp( name, "exclusive" ) == 0 )
                    return STATE_EXCLUSIVE;
                else if ( strcmp( name, "shared" ) == 0 )
                    return STATE_SHARED;
                break;

            case STATE_LOCKTYPE:
                if ( strcmp( name, "write" ) == 0 )
                    return STATE_WRITE;
                break;

            case STATE_LOCKTOKEN:
                if ( strcmp( name, "href" ) == 0 )
                    return STATE_HREF;
                break;

            case STATE_OWNER:
                // owner elem contains ANY. Accept anything; no state change.
                return STATE_OWNER;
        }
    }
    return NE_XML_DECLINE;
}

//////////////////////////////////////////////////////////////////////////
extern "C" int LockSequence_chardata_callback(
    void *userdata,
    int state,
    const char *buf,
    size_t len )
{
    LockSequenceParseContext * pCtx
                    = static_cast< LockSequenceParseContext * >( userdata );
    if ( !pCtx->pLock )
        pCtx->pLock = new ucb::Lock;

    switch ( state )
    {
        case STATE_DEPTH:
            if ( rtl_str_compareIgnoreAsciiCase_WithLength(
                    buf, len, "0", 1 ) == 0 )
            {
                pCtx->pLock->Depth = ucb::LockDepth_ZERO;
                pCtx->hasDepth = true;
            }
            else if ( rtl_str_compareIgnoreAsciiCase_WithLength(
                    buf, len, "1", 1 ) == 0 )
            {
                pCtx->pLock->Depth = ucb::LockDepth_ONE;
                pCtx->hasDepth = true;
            }
            else if ( rtl_str_compareIgnoreAsciiCase_WithLength(
                    buf, len, "infinity", 8 ) == 0 )
            {
                pCtx->pLock->Depth = ucb::LockDepth_INFINITY;
                pCtx->hasDepth = true;
            }
            else
                OSL_ENSURE( sal_False,
                            "LockSequence_chardata_callback - Unknown depth!" );
            break;

        case STATE_OWNER:
        {
            // collect raw XML data... (owner contains ANY)
            rtl::OUString aValue;
            pCtx->pLock->Owner >>= aValue;
            aValue += rtl::OUString( buf, len, RTL_TEXTENCODING_ASCII_US );
            pCtx->pLock->Owner <<= aValue;
            break;
        }

        case STATE_TIMEOUT:
            //
            //  RFC2518, RFC2616:
            //
            //  TimeType = ("Second-" DAVTimeOutVal | "Infinite" | Other)
            //  DAVTimeOutVal = 1*digit
            //  Other = "Extend" field-value
            //  field-value = *( field-content | LWS )
            //  field-content = <the OCTETs making up the field-value
            //                  and consisting of either *TEXT or combinations
            //                  of token, separators, and quoted-string>

            if ( rtl_str_compareIgnoreAsciiCase_WithLength(
                    buf, len, "Infinite", 8 ) == 0 )
            {
                pCtx->pLock->Timeout = sal_Int64( -1 );
                pCtx->hasTimeout = true;
            }
            else if ( rtl_str_shortenedCompareIgnoreAsciiCase_WithLength(
                                            buf, len, "Second-", 7, 7 ) == 0 )
            {
                pCtx->pLock->Timeout
                    = rtl::OString( buf + 7, len - 7 ).toInt64();
                pCtx->hasTimeout = true;
            }
//          else if ( rtl_str_shortenedCompareIgnoreCase_WithLength(
//                                          buf, len, "Extend", 6, 6 ) == 0 )
//          {
//              @@@
//          }
            else
            {
                pCtx->pLock->Timeout = sal_Int64( -1 );
                pCtx->hasTimeout = true;
                OSL_ENSURE( sal_False,
                        "LockSequence_chardata_callback - Unknown timeout!" );
            }
            break;

        case STATE_HREF:
        {
            // collect hrefs.
            sal_Int32 nPos = pCtx->pLock->LockTokens.getLength();
            pCtx->pLock->LockTokens.realloc( nPos + 1 );
            pCtx->pLock->LockTokens[ nPos ]
                = rtl::OUString( buf, len, RTL_TEXTENCODING_ASCII_US );
            pCtx->hasHREF = true;
            break;
        }

    }
    return 0; // zero to continue, non-zero to abort parsing
}

//////////////////////////////////////////////////////////////////////////
extern "C" int LockSequence_endelement_callback(
    void *userdata,
    int state,
    const char *,
    const char * )
{
    LockSequenceParseContext * pCtx
                    = static_cast< LockSequenceParseContext * >( userdata );
    if ( !pCtx->pLock )
        pCtx->pLock = new ucb::Lock;

    switch ( state )
    {
        case STATE_EXCLUSIVE:
            pCtx->pLock->Scope = ucb::LockScope_EXCLUSIVE;
            pCtx->hasLockScope = true;
            break;

        case STATE_SHARED:
            pCtx->pLock->Scope = ucb::LockScope_SHARED;
            pCtx->hasLockScope = true;
            break;

        case STATE_WRITE:
            pCtx->pLock->Type = ucb::LockType_WRITE;
            pCtx->hasLockType = true;
            break;

        case STATE_DEPTH:
            if ( !pCtx->hasDepth )
                return 1; // abort
            break;

        case STATE_HREF:
            if ( !pCtx->hasHREF )
                return 1; // abort
            break;

        case STATE_TIMEOUT:
            if ( !pCtx->hasTimeout )
                return 1; // abort
            break;

        case STATE_LOCKSCOPE:
            if ( !pCtx->hasLockScope )
                return 1; // abort
            break;

        case STATE_LOCKTYPE:
            if ( !pCtx->hasLockType )
                return 1; // abort
            break;

        case STATE_ACTIVELOCK:
            if ( !pCtx->hasLockType || !pCtx->hasLockType || !pCtx->hasDepth )
                return 1; // abort
            break;

        default:
            break;
    }
    return 0; // zero to continue, non-zero to abort parsing
}

//////////////////////////////////////////////////////////////////////////
// static
bool LockSequence::createFromXML( const rtl::OString & rInData,
                                  uno::Sequence< ucb::Lock > & rOutData )
{
    const sal_Int32 TOKEN_LENGTH = 13; // </activelock>
    bool success = true;

    // rInData may contain multiple <activelock>...</activelock> tags.
    sal_Int32 nCount = 0;
    sal_Int32 nStart = 0;
    sal_Int32 nEnd   = rInData.indexOf( "</activelock>" );
    while ( nEnd > -1 )
    {
        ne_xml_parser * parser = ne_xml_create();
        if ( !parser )
        {
            success = false;
            break;
        }

        LockSequenceParseContext aCtx;
        ne_xml_push_handler( parser,
                             LockSequence_startelement_callback,
                             LockSequence_chardata_callback,
                             LockSequence_endelement_callback,
                             &aCtx );

        ne_xml_parse( parser,
                      rInData.getStr() + nStart,
                      nEnd - nStart + TOKEN_LENGTH );

#if NEON_VERSION >= 0x0250
        success = !ne_xml_failed( parser );
#else
        success = !!ne_xml_valid( parser );
#endif

        ne_xml_destroy( parser );

        if ( !success )
            break;

        if ( aCtx.pLock )
        {
            nCount++;
            if ( nCount > rOutData.getLength() )
                rOutData.realloc( rOutData.getLength() + 1 );

            rOutData[ nCount - 1 ] = *aCtx.pLock;
        }

        nStart = nEnd + TOKEN_LENGTH;
        nEnd   = rInData.indexOf( "</activelock>", nStart );
    }

    return success;
}
<commit_msg>#i114622# - Workaround for buggy Oracle Beehive server response for lockdiscovery property.<commit_after>/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * Copyright 2000, 2010 Oracle and/or its affiliates.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org 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 version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/

// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_ucb.hxx"

#include <string.h>
#include <ne_xml.h>
#include "LockSequence.hxx"

using namespace webdav_ucp;
using namespace com::sun::star;

#define BEEHIVE_BUGS_WORKAROUND

//////////////////////////////////////////////////////////////////////////

struct LockSequenceParseContext
{
    ucb::Lock * pLock;
    bool hasLockScope;
    bool hasLockType;
    bool hasDepth;
    bool hasHREF;
    bool hasTimeout;

    LockSequenceParseContext()
    : pLock( 0 ), hasLockScope( false ), hasLockType( false ),
      hasDepth( false ), hasHREF( false ), hasTimeout( false ) {}

    ~LockSequenceParseContext() { delete pLock; }
};

#define STATE_TOP (1)

#define STATE_ACTIVELOCK    (STATE_TOP)
#define STATE_LOCKSCOPE     (STATE_TOP + 1)
#define STATE_LOCKTYPE      (STATE_TOP + 2)
#define STATE_DEPTH         (STATE_TOP + 3)
#define STATE_OWNER         (STATE_TOP + 4)
#define STATE_TIMEOUT       (STATE_TOP + 5)
#define STATE_LOCKTOKEN     (STATE_TOP + 6)
#define STATE_EXCLUSIVE     (STATE_TOP + 7)
#define STATE_SHARED        (STATE_TOP + 8)
#define STATE_WRITE         (STATE_TOP + 9)
#define STATE_HREF          (STATE_TOP + 10)

//////////////////////////////////////////////////////////////////////////
extern "C" int LockSequence_startelement_callback(
    void *,
    int parent,
    const char * /*nspace*/,
    const char *name,
    const char ** )
{
    if ( name != 0 )
    {
        switch ( parent )
        {
            case NE_XML_STATEROOT:
                if ( strcmp( name, "activelock" ) == 0 )
                    return STATE_ACTIVELOCK;
                break;

            case STATE_ACTIVELOCK:
                if ( strcmp( name, "lockscope" ) == 0 )
                    return STATE_LOCKSCOPE;
                else if ( strcmp( name, "locktype" ) == 0 )
                    return STATE_LOCKTYPE;
                else if ( strcmp( name, "depth" ) == 0 )
                    return STATE_DEPTH;
                else if ( strcmp( name, "owner" ) == 0 )
                    return STATE_OWNER;
                else if ( strcmp( name, "timeout" ) == 0 )
                    return STATE_TIMEOUT;
                else if ( strcmp( name, "locktoken" ) == 0 )
                    return STATE_LOCKTOKEN;
                break;

            case STATE_LOCKSCOPE:
                if ( strcmp( name, "exclusive" ) == 0 )
                    return STATE_EXCLUSIVE;
                else if ( strcmp( name, "shared" ) == 0 )
                    return STATE_SHARED;
                break;

            case STATE_LOCKTYPE:
                if ( strcmp( name, "write" ) == 0 )
                    return STATE_WRITE;
                break;

            case STATE_LOCKTOKEN:
                if ( strcmp( name, "href" ) == 0 )
                    return STATE_HREF;
                break;

            case STATE_OWNER:
                // owner elem contains ANY. Accept anything; no state change.
                return STATE_OWNER;
        }
    }
    return NE_XML_DECLINE;
}

//////////////////////////////////////////////////////////////////////////
extern "C" int LockSequence_chardata_callback(
    void *userdata,
    int state,
#ifdef BEEHIVE_BUGS_WORKAROUND
    const char *buf1,
#else
    const char *buf,
#endif
    size_t len )
{
    LockSequenceParseContext * pCtx
                    = static_cast< LockSequenceParseContext * >( userdata );
    if ( !pCtx->pLock )
        pCtx->pLock = new ucb::Lock;

#ifdef BEEHIVE_BUGS_WORKAROUND
    // Beehive sends XML values containing trailing newlines.
    if ( buf1[ len - 1 ] == 0x0a )
        len--;

    char * buf = new char[ len + 1 ]();
    strncpy( buf, buf1, len );
#endif

    switch ( state )
    {
        case STATE_DEPTH:
            if ( rtl_str_compareIgnoreAsciiCase_WithLength(
                    buf, len, "0", 1 ) == 0 )
            {
                pCtx->pLock->Depth = ucb::LockDepth_ZERO;
                pCtx->hasDepth = true;
            }
            else if ( rtl_str_compareIgnoreAsciiCase_WithLength(
                    buf, len, "1", 1 ) == 0 )
            {
                pCtx->pLock->Depth = ucb::LockDepth_ONE;
                pCtx->hasDepth = true;
            }
            else if ( rtl_str_compareIgnoreAsciiCase_WithLength(
                    buf, len, "infinity", 8 ) == 0 )
            {
                pCtx->pLock->Depth = ucb::LockDepth_INFINITY;
                pCtx->hasDepth = true;
            }
            else
                OSL_ENSURE( sal_False,
                            "LockSequence_chardata_callback - Unknown depth!" );
            break;

        case STATE_OWNER:
        {
            // collect raw XML data... (owner contains ANY)
            rtl::OUString aValue;
            pCtx->pLock->Owner >>= aValue;
            aValue += rtl::OUString( buf, len, RTL_TEXTENCODING_ASCII_US );
            pCtx->pLock->Owner <<= aValue;
            break;
        }

        case STATE_TIMEOUT:
            //
            //  RFC2518, RFC2616:
            //
            //  TimeType = ("Second-" DAVTimeOutVal | "Infinite" | Other)
            //  DAVTimeOutVal = 1*digit
            //  Other = "Extend" field-value
            //  field-value = *( field-content | LWS )
            //  field-content = <the OCTETs making up the field-value
            //                  and consisting of either *TEXT or combinations
            //                  of token, separators, and quoted-string>

            if ( rtl_str_compareIgnoreAsciiCase_WithLength(
                    buf, len, "Infinite", 8 ) == 0 )
            {
                pCtx->pLock->Timeout = sal_Int64( -1 );
                pCtx->hasTimeout = true;
            }
            else if ( rtl_str_shortenedCompareIgnoreAsciiCase_WithLength(
                                            buf, len, "Second-", 7, 7 ) == 0 )
            {
                pCtx->pLock->Timeout
                    = rtl::OString( buf + 7, len - 7 ).toInt64();
                pCtx->hasTimeout = true;
            }
//          else if ( rtl_str_shortenedCompareIgnoreCase_WithLength(
//                                          buf, len, "Extend", 6, 6 ) == 0 )
//          {
//              @@@
//          }
            else
                OSL_ENSURE( sal_False,
                        "LockSequence_chardata_callback - Unknown timeout!" );
            break;

        case STATE_HREF:
        {
            // collect hrefs.
            sal_Int32 nPos = pCtx->pLock->LockTokens.getLength();
            pCtx->pLock->LockTokens.realloc( nPos + 1 );
            pCtx->pLock->LockTokens[ nPos ]
                = rtl::OUString( buf, len, RTL_TEXTENCODING_ASCII_US );
            pCtx->hasHREF = true;
            break;
        }

    }

#ifdef BEEHIVE_BUGS_WORKAROUND
    delete [] buf;
#endif

    return 0; // zero to continue, non-zero to abort parsing
}

//////////////////////////////////////////////////////////////////////////
extern "C" int LockSequence_endelement_callback(
    void *userdata,
    int state,
    const char *,
    const char * )
{
    LockSequenceParseContext * pCtx
                    = static_cast< LockSequenceParseContext * >( userdata );
    if ( !pCtx->pLock )
        pCtx->pLock = new ucb::Lock;

    switch ( state )
    {
        case STATE_EXCLUSIVE:
            pCtx->pLock->Scope = ucb::LockScope_EXCLUSIVE;
            pCtx->hasLockScope = true;
            break;

        case STATE_SHARED:
            pCtx->pLock->Scope = ucb::LockScope_SHARED;
            pCtx->hasLockScope = true;
            break;

        case STATE_WRITE:
            pCtx->pLock->Type = ucb::LockType_WRITE;
            pCtx->hasLockType = true;
            break;

        case STATE_DEPTH:
            if ( !pCtx->hasDepth )
                return 1; // abort
            break;

        case STATE_HREF:
            if ( !pCtx->hasHREF )
                return 1; // abort
            break;

        case STATE_TIMEOUT:
            if ( !pCtx->hasTimeout )
                return 1; // abort
            break;

        case STATE_LOCKSCOPE:
            if ( !pCtx->hasLockScope )
                return 1; // abort
            break;

        case STATE_LOCKTYPE:
            if ( !pCtx->hasLockType )
                return 1; // abort
            break;

        case STATE_ACTIVELOCK:
            if ( !pCtx->hasLockType || !pCtx->hasLockType || !pCtx->hasDepth )
                return 1; // abort
            break;

        default:
            break;
    }
    return 0; // zero to continue, non-zero to abort parsing
}

//////////////////////////////////////////////////////////////////////////
// static
bool LockSequence::createFromXML( const rtl::OString & rInData,
                                  uno::Sequence< ucb::Lock > & rOutData )
{
    const sal_Int32 TOKEN_LENGTH = 13; // </activelock>
    bool success = true;

    // rInData may contain multiple <activelock>...</activelock> tags.
    sal_Int32 nCount = 0;
    sal_Int32 nStart = 0;
    sal_Int32 nEnd   = rInData.indexOf( "</activelock>" );
    while ( nEnd > -1 )
    {
        ne_xml_parser * parser = ne_xml_create();
        if ( !parser )
        {
            success = false;
            break;
        }

        LockSequenceParseContext aCtx;
        ne_xml_push_handler( parser,
                             LockSequence_startelement_callback,
                             LockSequence_chardata_callback,
                             LockSequence_endelement_callback,
                             &aCtx );

        ne_xml_parse( parser,
                      rInData.getStr() + nStart,
                      nEnd - nStart + TOKEN_LENGTH );

#if NEON_VERSION >= 0x0250
        success = !ne_xml_failed( parser );
#else
        success = !!ne_xml_valid( parser );
#endif

        ne_xml_destroy( parser );

        if ( !success )
            break;

        if ( aCtx.pLock )
        {
            nCount++;
            if ( nCount > rOutData.getLength() )
                rOutData.realloc( rOutData.getLength() + 1 );

            rOutData[ nCount - 1 ] = *aCtx.pLock;
        }

        nStart = nEnd + TOKEN_LENGTH;
        nEnd   = rInData.indexOf( "</activelock>", nStart );
    }

    return success;
}
<|endoftext|>


<commit_before>// Copyright 2016 lyobzik
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "PrimitiveTypes.h"

#include <JsonSchema.h>
#include <JsonResolver.h>

#include "../Regex.h"
#include "../ValidationContext.h"

namespace JsonSchemaValidator {

JsonString::JsonString(JsonValue const &schema, JsonResolverPtr const &resolver,
                       std::string const &path)
	: JsonTypeImpl(schema, resolver, path)
	, min_length_(std::numeric_limits<size_t>::lowest())
	, max_length_(std::numeric_limits<size_t>::max())
	, pattern_() {

	GetChildValue(schema, "minLength", min_length_);
	GetChildValue(schema, "maxLength", max_length_);

	char const *pattern = nullptr;
	if (GetChildValue(schema, "pattern", pattern)) {
		pattern_ = Regex::Create(pattern);
	}
}

bool JsonString::CheckValueType(JsonValue const &json) const {
	return json.IsString();
}

void JsonString::CheckTypeRestrictions(JsonValue const &json, ValidationContext &context) const {
	if (json.GetStringLength() < min_length_) {
		return RaiseError<MinimalLengthError>(context, min_length_);
	}
	if (json.GetStringLength() > max_length_) {
		return RaiseError<MaximalLengthError>(context, max_length_);
	}

	if (pattern_ && !pattern_->IsCorrespond(GetValue<char const *>(json))) {
		return RaiseError<PatternError>(context, pattern_->Pattern());
	}
}

///////////////////////////////////////////////////////////////////////////////////////////////////
template <typename Type>
JsonBaseNumber<Type>::JsonBaseNumber(JsonValue const &schema, JsonResolverPtr const &resolver,
                                     std::string const &path)
	: Parent(schema, resolver, path)
	, minimum_(std::numeric_limits<typename Parent::ValueType>::lowest())
	, maximum_(std::numeric_limits<typename Parent::ValueType>::max())
	, exclusive_minimum_(false)
	, exclusive_maximum_(false)
	, divisible_by_() {

	GetChildValue(schema, "minimum", minimum_);
	GetChildValue(schema, "maximum", maximum_);

	GetChildValue(schema, "exclusiveMinimum", exclusive_minimum_);
	GetChildValue(schema, "exclusiveMaximum", exclusive_maximum_);

	GetChildValue(schema, "divisibleBy", divisible_by_);
}

template <>
bool JsonBaseNumber<long long>::IsEqual(long long left, long long right) {
	return left == right;
}

template <>
bool JsonBaseNumber<double>::IsEqual(double left, double right) {
	return std::fabs(left - right) <= std::numeric_limits<double>::epsilon();
}

///////////////////////////////////////////////////////////////////////////////////////////////////
JsonNumber::JsonNumber(JsonValue const &schema, JsonResolverPtr const &resolver,
                       std::string const &path)
	: JsonBaseNumber(schema, resolver, path) {
}

bool JsonNumber::CheckValueType(JsonValue const &json) const {
	return json.IsNumber();
}

///////////////////////////////////////////////////////////////////////////////////////////////////
JsonInteger::JsonInteger(JsonValue const &schema, JsonResolverPtr const &resolver,
                         std::string const &path)
	: JsonBaseNumber(schema, resolver, path) {
}

bool JsonInteger::CheckValueType(JsonValue const &json) const {
	return json.IsInt64() || json.IsUint64() || json.IsInt() || json.IsUint();
}

///////////////////////////////////////////////////////////////////////////////////////////////////
JsonBoolean::JsonBoolean(JsonValue const &schema, JsonResolverPtr const &resolver,
                         std::string const &path)
	: JsonTypeImpl(schema, resolver, path) {
}

bool JsonBoolean::CheckValueType(JsonValue const &json) const {
	return json.IsBool();
}

void JsonBoolean::CheckTypeRestrictions(JsonValue const &/*json*/,
	                                    ValidationContext &/*context*/) const {
}

///////////////////////////////////////////////////////////////////////////////////////////////////
JsonNull::JsonNull(JsonValue const &schema, JsonResolverPtr const &resolver,
                   std::string const &path)
	: JsonTypeImpl(schema, resolver, path) {
}

bool JsonNull::CheckValueType(JsonValue const &json) const {
	return json.IsNull();
}

void JsonNull::CheckTypeRestrictions(JsonValue const &/*json*/,
                                     ValidationContext &/*context*/) const {
}

///////////////////////////////////////////////////////////////////////////////////////////////////
JsonObject::JsonObject(JsonValue const &schema, JsonResolverPtr const &resolver,
                       std::string const &path)
	: JsonTypeImpl(schema, resolver, path)
	, properties_()
	, pattern_properties_()
	, may_contains_additional_properties_()
	, additional_properties_()
	, simple_dependencies_()
	, schema_dependencies_() {

	for (auto const &member : GetMembers(schema, "properties")) {
		properties_.insert({ GetValue<char const *>(member.name),
		                     CreateMember(member, resolver) });
	}

	for (auto const &member : GetMembers(schema, "patternProperties")) {
		pattern_properties_.push_back({ Regex::Create(GetValue<char const *>(member.name)),
		                                CreateMember(member, resolver) });
	}

	GetChildValue(schema, "additionalProperties", may_contains_additional_properties_);
	if (!may_contains_additional_properties_.exists) {
		GetChildValue(schema, "additionalProperties", additional_properties_, resolver, path);
	}

	for (auto const &member : GetMembers(schema, "dependencies")) {
		if (member.value.IsObject()) {
			schema_dependencies_.insert({ GetValue<char const *>(member.name),
			                              CreateMember(member, resolver) });
		}
		else if (member.value.IsArray()) {
			std::set<char const *, StrLess> strings;
			for (rapidjson::SizeType j = 0; j < member.value.Size(); ++j) {
				auto insert_result = strings.insert(GetValue<char const *>(member.value[j]));
				if (!insert_result.second) {
					throw IncorrectSchema(SchemaErrors::IncorrectDependencies);
				}
			}
			simple_dependencies_.insert({ GetValue<char const *>(member.name), strings });
		}
		else if (member.value.IsString()) {
			simple_dependencies_.insert({ GetValue<char const *>(member.name),
			                            { GetValue<char const *>(member.value) } });
		}
		else {
			throw IncorrectSchema(SchemaErrors::IncorrectDependencies);
		}
	}
}

bool JsonObject::CheckValueType(JsonValue const &json) const {
	return json.IsObject();
}

void JsonObject::CheckTypeRestrictions(JsonValue const &json, ValidationContext &context) const {
	for (auto const &member : GetMembers(json)) {
		bool described_property = false;
		char const *name = GetValue<char const *>(member.name);
		MemberPathHolder path_holder(name, context);
		auto propertyIt = properties_.find(name);
		if (propertyIt != properties_.end()) {
			propertyIt->second->Validate(member.value, context);
			if (!context.GetResult()) return;
			described_property = true;
		}
		for (auto const &pattern_property : pattern_properties_) {
			if (pattern_property.first->IsCorrespond(name)) {
				pattern_property.second->Validate(member.value, context);
				if (!context.GetResult()) return;
				described_property = true;
			}
		}

		if (!described_property) {
			if (may_contains_additional_properties_.exists) {
				if (!may_contains_additional_properties_.value) {
					path_holder.Reset();
					return RaiseError<AdditionalPropertyError>(context, name);
				}
			}
			else if (additional_properties_.exists) {
				additional_properties_.value->Validate(member.value, context);
				if (!context.GetResult()) return;
			}
		}

		if (!simple_dependencies_.empty()) {
			auto simpleArrayDependIt = simple_dependencies_.find(name);
			if (simpleArrayDependIt != simple_dependencies_.end()) {
				for (auto const &depend : simpleArrayDependIt->second) {
					if (!json.HasMember(depend)) {
						return RaiseError<DependenciesRestrictionsError>(context, name);
					}
				}
			}
		}
		if (!schema_dependencies_.empty()) {
			auto schemaDependIt = schema_dependencies_.find(name);
			if (schemaDependIt != schema_dependencies_.end()) {
				schemaDependIt->second->Validate(json, context);
				if (!context.GetResult()) return;
			}
		}
		path_holder.Reset();
	}
	for (auto const &property : properties_) {
		if (property.second->IsRequired() && !json.HasMember(property.first)) {
			return RaiseError<RequiredPropertyError>(context, property.first);
		}
	}
}

JsonTypePtr JsonObject::CreateMember(JsonValueMember const &member,
                                     JsonResolverPtr const &resolver) const {
	return JsonType::Create(member.value, resolver,
	                        MemberPath(GetValue<char const *>(member.name)));
}

///////////////////////////////////////////////////////////////////////////////////////////////////
JsonArray::JsonArray(JsonValue const &schema, JsonResolverPtr const &resolver,
                     std::string const &path)
	: JsonTypeImpl(schema, resolver, path)
	, min_items_(0)
	, max_items_(std::numeric_limits<size_t>::max())
	, unique_items_(false)
	, items_()
	, items_array_()
	, may_contains_additional_items_()
	, additional_items_() {

	GetChildValue(schema, "minItems", min_items_);
	GetChildValue(schema, "maxItems", max_items_);

	GetChildValue(schema, "uniqueItems", unique_items_);

	GetChildValue(schema, "items", items_, resolver, path);
	GetChildValue(schema, "items", items_array_, resolver, path);

	GetChildValue(schema, "additionalItems", may_contains_additional_items_);
	if (!may_contains_additional_items_.exists) {
		GetChildValue(schema, "additionalItems", additional_items_, resolver, path);
	}
}

bool JsonArray::CheckValueType(JsonValue const &json) const {
	return json.IsArray();
}

void JsonArray::CheckTypeRestrictions(JsonValue const &json, ValidationContext &context) const {
	if (json.Size() < min_items_) {
		return RaiseError<MinimalItemsCountError>(context, min_items_);
	}
	if (json.Size() > max_items_) {
		return RaiseError<MaximalItemsCountError>(context, max_items_);
	}

	if (unique_items_) {
		for (rapidjson::SizeType i = 0; i < json.Size() - 1; ++i) {
			for (rapidjson::SizeType j = i + 1; j < json.Size(); ++j) {
				if (IsEqual(json[i], json[j])) {
					return RaiseError<UniqueItemsError>(context);
				}
			}
		}
	}

	if (items_.exists) {
		for (rapidjson::SizeType i = 0; i < json.Size(); ++i) {
			ElementPathHolder path_holder(i, context);
			items_.value->Validate(json[i], context);
			if (!context.GetResult()) return;
			path_holder.Reset();
		}
	}
	else if (items_array_.exists) {
		rapidjson::SizeType i = 0;
		for (; i < json.Size() && i < items_array_.value.size(); ++i) {
			ElementPathHolder path_holder(i, context);
			items_array_.value[i]->Validate(json[i], context);
			if (!context.GetResult()) return;
			path_holder.Reset();
		}
		if (i < json.Size()) {
			if (may_contains_additional_items_.exists) {
				if (!may_contains_additional_items_.value) {
					return RaiseError<AdditionalItemsError>(context);
				}
			}
			else if (additional_items_.exists) {
				for (; i < json.Size(); ++i) {
					ElementPathHolder path_holder(i, context);
					additional_items_.value->Validate(json[i], context);
					if (!context.GetResult()) return;
					path_holder.Reset();
				}
			}
		}
	}
}

} // namespace JsonSchemaValidator
<commit_msg>Fix checking of unique restriction of empty array<commit_after>// Copyright 2016 lyobzik
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "PrimitiveTypes.h"

#include <JsonSchema.h>
#include <JsonResolver.h>

#include "../Regex.h"
#include "../ValidationContext.h"

namespace JsonSchemaValidator {

JsonString::JsonString(JsonValue const &schema, JsonResolverPtr const &resolver,
                       std::string const &path)
	: JsonTypeImpl(schema, resolver, path)
	, min_length_(std::numeric_limits<size_t>::lowest())
	, max_length_(std::numeric_limits<size_t>::max())
	, pattern_() {

	GetChildValue(schema, "minLength", min_length_);
	GetChildValue(schema, "maxLength", max_length_);

	char const *pattern = nullptr;
	if (GetChildValue(schema, "pattern", pattern)) {
		pattern_ = Regex::Create(pattern);
	}
}

bool JsonString::CheckValueType(JsonValue const &json) const {
	return json.IsString();
}

void JsonString::CheckTypeRestrictions(JsonValue const &json, ValidationContext &context) const {
	if (json.GetStringLength() < min_length_) {
		return RaiseError<MinimalLengthError>(context, min_length_);
	}
	if (json.GetStringLength() > max_length_) {
		return RaiseError<MaximalLengthError>(context, max_length_);
	}

	if (pattern_ && !pattern_->IsCorrespond(GetValue<char const *>(json))) {
		return RaiseError<PatternError>(context, pattern_->Pattern());
	}
}

///////////////////////////////////////////////////////////////////////////////////////////////////
template <typename Type>
JsonBaseNumber<Type>::JsonBaseNumber(JsonValue const &schema, JsonResolverPtr const &resolver,
                                     std::string const &path)
	: Parent(schema, resolver, path)
	, minimum_(std::numeric_limits<typename Parent::ValueType>::lowest())
	, maximum_(std::numeric_limits<typename Parent::ValueType>::max())
	, exclusive_minimum_(false)
	, exclusive_maximum_(false)
	, divisible_by_() {

	GetChildValue(schema, "minimum", minimum_);
	GetChildValue(schema, "maximum", maximum_);

	GetChildValue(schema, "exclusiveMinimum", exclusive_minimum_);
	GetChildValue(schema, "exclusiveMaximum", exclusive_maximum_);

	GetChildValue(schema, "divisibleBy", divisible_by_);
}

template <>
bool JsonBaseNumber<long long>::IsEqual(long long left, long long right) {
	return left == right;
}

template <>
bool JsonBaseNumber<double>::IsEqual(double left, double right) {
	return std::fabs(left - right) <= std::numeric_limits<double>::epsilon();
}

///////////////////////////////////////////////////////////////////////////////////////////////////
JsonNumber::JsonNumber(JsonValue const &schema, JsonResolverPtr const &resolver,
                       std::string const &path)
	: JsonBaseNumber(schema, resolver, path) {
}

bool JsonNumber::CheckValueType(JsonValue const &json) const {
	return json.IsNumber();
}

///////////////////////////////////////////////////////////////////////////////////////////////////
JsonInteger::JsonInteger(JsonValue const &schema, JsonResolverPtr const &resolver,
                         std::string const &path)
	: JsonBaseNumber(schema, resolver, path) {
}

bool JsonInteger::CheckValueType(JsonValue const &json) const {
	return json.IsInt64() || json.IsUint64() || json.IsInt() || json.IsUint();
}

///////////////////////////////////////////////////////////////////////////////////////////////////
JsonBoolean::JsonBoolean(JsonValue const &schema, JsonResolverPtr const &resolver,
                         std::string const &path)
	: JsonTypeImpl(schema, resolver, path) {
}

bool JsonBoolean::CheckValueType(JsonValue const &json) const {
	return json.IsBool();
}

void JsonBoolean::CheckTypeRestrictions(JsonValue const &/*json*/,
	                                    ValidationContext &/*context*/) const {
}

///////////////////////////////////////////////////////////////////////////////////////////////////
JsonNull::JsonNull(JsonValue const &schema, JsonResolverPtr const &resolver,
                   std::string const &path)
	: JsonTypeImpl(schema, resolver, path) {
}

bool JsonNull::CheckValueType(JsonValue const &json) const {
	return json.IsNull();
}

void JsonNull::CheckTypeRestrictions(JsonValue const &/*json*/,
                                     ValidationContext &/*context*/) const {
}

///////////////////////////////////////////////////////////////////////////////////////////////////
JsonObject::JsonObject(JsonValue const &schema, JsonResolverPtr const &resolver,
                       std::string const &path)
	: JsonTypeImpl(schema, resolver, path)
	, properties_()
	, pattern_properties_()
	, may_contains_additional_properties_()
	, additional_properties_()
	, simple_dependencies_()
	, schema_dependencies_() {

	for (auto const &member : GetMembers(schema, "properties")) {
		properties_.insert({ GetValue<char const *>(member.name),
		                     CreateMember(member, resolver) });
	}

	for (auto const &member : GetMembers(schema, "patternProperties")) {
		pattern_properties_.push_back({ Regex::Create(GetValue<char const *>(member.name)),
		                                CreateMember(member, resolver) });
	}

	GetChildValue(schema, "additionalProperties", may_contains_additional_properties_);
	if (!may_contains_additional_properties_.exists) {
		GetChildValue(schema, "additionalProperties", additional_properties_, resolver, path);
	}

	for (auto const &member : GetMembers(schema, "dependencies")) {
		if (member.value.IsObject()) {
			schema_dependencies_.insert({ GetValue<char const *>(member.name),
			                              CreateMember(member, resolver) });
		}
		else if (member.value.IsArray()) {
			std::set<char const *, StrLess> strings;
			for (rapidjson::SizeType j = 0; j < member.value.Size(); ++j) {
				auto insert_result = strings.insert(GetValue<char const *>(member.value[j]));
				if (!insert_result.second) {
					throw IncorrectSchema(SchemaErrors::IncorrectDependencies);
				}
			}
			simple_dependencies_.insert({ GetValue<char const *>(member.name), strings });
		}
		else if (member.value.IsString()) {
			simple_dependencies_.insert({ GetValue<char const *>(member.name),
			                            { GetValue<char const *>(member.value) } });
		}
		else {
			throw IncorrectSchema(SchemaErrors::IncorrectDependencies);
		}
	}
}

bool JsonObject::CheckValueType(JsonValue const &json) const {
	return json.IsObject();
}

void JsonObject::CheckTypeRestrictions(JsonValue const &json, ValidationContext &context) const {
	for (auto const &member : GetMembers(json)) {
		bool described_property = false;
		char const *name = GetValue<char const *>(member.name);
		MemberPathHolder path_holder(name, context);
		auto propertyIt = properties_.find(name);
		if (propertyIt != properties_.end()) {
			propertyIt->second->Validate(member.value, context);
			if (!context.GetResult()) return;
			described_property = true;
		}
		for (auto const &pattern_property : pattern_properties_) {
			if (pattern_property.first->IsCorrespond(name)) {
				pattern_property.second->Validate(member.value, context);
				if (!context.GetResult()) return;
				described_property = true;
			}
		}

		if (!described_property) {
			if (may_contains_additional_properties_.exists) {
				if (!may_contains_additional_properties_.value) {
					path_holder.Reset();
					return RaiseError<AdditionalPropertyError>(context, name);
				}
			}
			else if (additional_properties_.exists) {
				additional_properties_.value->Validate(member.value, context);
				if (!context.GetResult()) return;
			}
		}

		if (!simple_dependencies_.empty()) {
			auto simpleArrayDependIt = simple_dependencies_.find(name);
			if (simpleArrayDependIt != simple_dependencies_.end()) {
				for (auto const &depend : simpleArrayDependIt->second) {
					if (!json.HasMember(depend)) {
						return RaiseError<DependenciesRestrictionsError>(context, name);
					}
				}
			}
		}
		if (!schema_dependencies_.empty()) {
			auto schemaDependIt = schema_dependencies_.find(name);
			if (schemaDependIt != schema_dependencies_.end()) {
				schemaDependIt->second->Validate(json, context);
				if (!context.GetResult()) return;
			}
		}
		path_holder.Reset();
	}
	for (auto const &property : properties_) {
		if (property.second->IsRequired() && !json.HasMember(property.first)) {
			return RaiseError<RequiredPropertyError>(context, property.first);
		}
	}
}

JsonTypePtr JsonObject::CreateMember(JsonValueMember const &member,
                                     JsonResolverPtr const &resolver) const {
	return JsonType::Create(member.value, resolver,
	                        MemberPath(GetValue<char const *>(member.name)));
}

///////////////////////////////////////////////////////////////////////////////////////////////////
JsonArray::JsonArray(JsonValue const &schema, JsonResolverPtr const &resolver,
                     std::string const &path)
	: JsonTypeImpl(schema, resolver, path)
	, min_items_(0)
	, max_items_(std::numeric_limits<size_t>::max())
	, unique_items_(false)
	, items_()
	, items_array_()
	, may_contains_additional_items_()
	, additional_items_() {

	GetChildValue(schema, "minItems", min_items_);
	GetChildValue(schema, "maxItems", max_items_);

	GetChildValue(schema, "uniqueItems", unique_items_);

	GetChildValue(schema, "items", items_, resolver, path);
	GetChildValue(schema, "items", items_array_, resolver, path);

	GetChildValue(schema, "additionalItems", may_contains_additional_items_);
	if (!may_contains_additional_items_.exists) {
		GetChildValue(schema, "additionalItems", additional_items_, resolver, path);
	}
}

bool JsonArray::CheckValueType(JsonValue const &json) const {
	return json.IsArray();
}

void JsonArray::CheckTypeRestrictions(JsonValue const &json, ValidationContext &context) const {
	if (json.Size() < min_items_) {
		return RaiseError<MinimalItemsCountError>(context, min_items_);
	}
	if (json.Size() > max_items_) {
		return RaiseError<MaximalItemsCountError>(context, max_items_);
	}

	if (unique_items_ && !json.Empty()) {
		for (rapidjson::SizeType i = 0; i < json.Size() - 1; ++i) {
			for (rapidjson::SizeType j = i + 1; j < json.Size(); ++j) {
				if (IsEqual(json[i], json[j])) {
					return RaiseError<UniqueItemsError>(context);
				}
			}
		}
	}

	if (items_.exists) {
		for (rapidjson::SizeType i = 0; i < json.Size(); ++i) {
			ElementPathHolder path_holder(i, context);
			items_.value->Validate(json[i], context);
			if (!context.GetResult()) return;
			path_holder.Reset();
		}
	}
	else if (items_array_.exists) {
		rapidjson::SizeType i = 0;
		for (; i < json.Size() && i < items_array_.value.size(); ++i) {
			ElementPathHolder path_holder(i, context);
			items_array_.value[i]->Validate(json[i], context);
			if (!context.GetResult()) return;
			path_holder.Reset();
		}
		if (i < json.Size()) {
			if (may_contains_additional_items_.exists) {
				if (!may_contains_additional_items_.value) {
					return RaiseError<AdditionalItemsError>(context);
				}
			}
			else if (additional_items_.exists) {
				for (; i < json.Size(); ++i) {
					ElementPathHolder path_holder(i, context);
					additional_items_.value->Validate(json[i], context);
					if (!context.GetResult()) return;
					path_holder.Reset();
				}
			}
		}
	}
}

} // namespace JsonSchemaValidator
<|endoftext|>