<commit_before>// Copyright (c) 2015
// Author: Chrono Law
#include <std.hpp>
//using namespace std;
using std::string;
using std::cout;
using std::endl;

#include <boost/foreach.hpp>
#include <boost/filesystem.hpp>
using namespace boost::filesystem;
namespace fs = boost::filesystem;

//////////////////////////////////////////
void case1()
{
    path p1("./a_dir");
    path p2("/usr/local/lib");
    path p3("c:\\tmp\\test.text");
    path p4("d:/boost/boost/filesystem/");

    path p5;
    assert(p5.empty());

}

//////////////////////////////////////////
void case2()
{
    char str[] = "the path is (/root).";

    {
        path p(str + 13, str + 14);
        assert(!p.empty());

        p /= "etc";
        string filename = "xinetd.conf";
        p.append(filename.begin(), filename.end());
        cout << p << endl;
        cout << system_complete(p) << endl;
    }

    {
        path p(str + 13, str + 15);

        p += "etc";
        string filename = "xinetd.conf";
        p.concat(filename.begin(), filename.end());
        cout << p << endl;
    }

    path p("/::/*/?/<>");
}

//////////////////////////////////////////
void case3()
{
    string fname("w+abc.xxx");
    assert(!portable_posix_name(fname));
    assert(windows_name(fname));

    assert(!portable_name("w+()abc.txt")    && !portable_name("./abc"));
    assert(!portable_directory_name("a.txt") && portable_directory_name("abc"));
    assert( portable_file_name("a.bc") && !portable_file_name("y.conf"));

}

//////////////////////////////////////////
void case4()
{
    path p("/usr/local/include/xxx.hpp");

    cout << p.string() << endl;

    cout << p.parent_path() << endl;
    cout << p.stem() << endl;
    cout << p.filename() << endl;
    cout << p.extension() << endl;

    assert(p.is_absolute());
    assert(system_complete(p).is_absolute());

    cout << p.root_name() << endl;
    cout << p.root_directory() << endl;
    cout << p.root_path() << endl;

    assert(!p.has_root_name());
    assert( p.has_root_path());
    assert( p.has_parent_path());

    cout << p.replace_extension() << endl;
    cout << p.replace_extension("hxx") << endl;
    cout << p.remove_filename() << endl;

    path p1("/test/1.cpp");
    path p2("/TEST/1.cpp");
    path p3("/abc/1.cpp");

    assert(p1 != p2);
    assert(p2 < p3);

    p = "/boost/tools/libs";

    BOOST_FOREACH(auto& x , p)
    {
            cout << "["<< x << "]";
    }

}

//////////////////////////////////////////
void case5()
{
    path p("/test.txt");

    try
    {
        file_size(p);
    }
    catch(filesystem_error& e)
    {
        cout << e.path1() << endl;
        cout << e.what() << endl;
    }

}

//////////////////////////////////////////
void case6()
{
    assert(status("/dev/null").type()   == character_file);
    assert(status("/bin").type()    == directory_file);
    assert(status("/bin/sh").type()     == regular_file);

    assert((status("/bin/sh").permissions() & owner_exe) == owner_exe);

    path root = "/usr/local/include/boost";

    assert( is_directory(root));
    assert(!exists(root/"nofile"));
    assert(!is_symlink(root/"version.hpp"));
    assert(!is_other(root/"version.hpp"));
    assert( is_regular_file(root/"version.hpp"));
    assert(!fs::is_empty(root/"version.hpp"));

}

//////////////////////////////////////////
void case7()
{
    cout << initial_path() << endl;
    cout << current_path() << endl;

    // generate a temp file for test
    std::ofstream("./test.txt") << "abcd" << std::endl;

    //path p("/usr/local/include/boost/version.hpp");
    path p("./test.txt");
    cout << file_size(p) << endl;

    time_t t = last_write_time(p);
    last_write_time(p, time(0));

    (void)t;
}

//////////////////////////////////////////
#include <boost/ratio.hpp>
void case8()
{
    using namespace boost;
    //const int GBYTES = 1000*1000*1000;          //GB，不是GiB
    space_info si = space("/home/chrono");
    cout << si.capacity / giga::num<< endl;
    cout << si.available / giga::num<< endl;
    cout << si.free / giga::num<< endl;
}

//////////////////////////////////////////
void case9()
{
    //namespace fs = boost::filesystem;

    path ptest = "./test";
    if (exists(ptest))
    {
        if (fs::is_empty(ptest))
        {
            remove(ptest) ;
        }
        else
        {
            remove_all(ptest);
        }
    }

    assert(!exists(ptest));
    create_directory(ptest) ;

    copy_file("/usr/local/include/boost/version.hpp", ptest / "a.txt");
    assert(exists(ptest / "a.txt"));

    rename(ptest / "a.txt", ptest / "b.txt");
    assert(exists(ptest / "b.txt"));

    create_directories(ptest / "sub_dir1" / "sub_dir1");
}

//////////////////////////////////////////
void case10()
{
    //directory_iterator end;
    //for (directory_iterator pos("/usr/local/lib/");pos != end; ++pos)
    //{       cout << *pos << endl;   }
    string path = "/dev/shm";//"/tmp";

    typedef std::pair<directory_iterator, directory_iterator> dir_range;
    //dir_range dr(directory_iterator("/usr/local/lib/"),
    dir_range dr(directory_iterator(path.c_str()),
    directory_iterator());

    BOOST_FOREACH(auto& x , dr)
    {      cout << x << endl;       }

    typedef recursive_directory_iterator rd_iterator;

    rd_iterator  end;
    for (rd_iterator pos(path.c_str());pos != end; ++pos)
    {
        cout << "level" << pos.level() << ":" <<*pos << endl;
    }

}

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

int main()
{
    case1();
    case2();
    case3();
    case4();
    //case5();
    case6();
    case7();
    case8();
    //case9();
    case10();
}

<commit_msg>level() => depth()<commit_after>// Copyright (c) 2015
// Author: Chrono Law
#include <std.hpp>
//using namespace std;
using std::string;
using std::cout;
using std::endl;

#include <boost/foreach.hpp>
#include <boost/filesystem.hpp>
using namespace boost::filesystem;
namespace fs = boost::filesystem;

//////////////////////////////////////////
void case1()
{
    path p1("./a_dir");
    path p2("/usr/local/lib");
    path p3("c:\\tmp\\test.text");
    path p4("d:/boost/boost/filesystem/");

    path p5;
    assert(p5.empty());

}

//////////////////////////////////////////
void case2()
{
    char str[] = "the path is (/root).";

    {
        path p(str + 13, str + 14);
        assert(!p.empty());

        p /= "etc";
        string filename = "xinetd.conf";
        p.append(filename.begin(), filename.end());
        cout << p << endl;
        cout << system_complete(p) << endl;
    }

    {
        path p(str + 13, str + 15);

        p += "etc";
        string filename = "xinetd.conf";
        p.concat(filename.begin(), filename.end());
        cout << p << endl;
    }

    path p("/::/*/?/<>");
}

//////////////////////////////////////////
void case3()
{
    string fname("w+abc.xxx");
    assert(!portable_posix_name(fname));
    assert(windows_name(fname));

    assert(!portable_name("w+()abc.txt")    && !portable_name("./abc"));
    assert(!portable_directory_name("a.txt") && portable_directory_name("abc"));
    assert( portable_file_name("a.bc") && !portable_file_name("y.conf"));

}

//////////////////////////////////////////
void case4()
{
    path p("/usr/local/include/xxx.hpp");

    cout << p.string() << endl;

    cout << p.parent_path() << endl;
    cout << p.stem() << endl;
    cout << p.filename() << endl;
    cout << p.extension() << endl;

    assert(p.is_absolute());
    assert(system_complete(p).is_absolute());

    cout << p.root_name() << endl;
    cout << p.root_directory() << endl;
    cout << p.root_path() << endl;

    assert(!p.has_root_name());
    assert( p.has_root_path());
    assert( p.has_parent_path());

    cout << p.replace_extension() << endl;
    cout << p.replace_extension("hxx") << endl;
    cout << p.remove_filename() << endl;

    path p1("/test/1.cpp");
    path p2("/TEST/1.cpp");
    path p3("/abc/1.cpp");

    assert(p1 != p2);
    assert(p2 < p3);

    p = "/boost/tools/libs";

    BOOST_FOREACH(auto& x , p)
    {
            cout << "["<< x << "]";
    }

}

//////////////////////////////////////////
void case5()
{
    path p("/test.txt");

    try
    {
        file_size(p);
    }
    catch(filesystem_error& e)
    {
        cout << e.path1() << endl;
        cout << e.what() << endl;
    }

}

//////////////////////////////////////////
void case6()
{
    assert(status("/dev/null").type()   == character_file);
    assert(status("/bin").type()    == directory_file);
    assert(status("/bin/sh").type()     == regular_file);

    assert((status("/bin/sh").permissions() & owner_exe) == owner_exe);

    path root = "/usr/local/include/boost";

    assert( is_directory(root));
    assert(!exists(root/"nofile"));
    assert(!is_symlink(root/"version.hpp"));
    assert(!is_other(root/"version.hpp"));
    assert( is_regular_file(root/"version.hpp"));
    assert(!fs::is_empty(root/"version.hpp"));

}

//////////////////////////////////////////
void case7()
{
    cout << initial_path() << endl;
    cout << current_path() << endl;

    // generate a temp file for test
    std::ofstream("./test.txt") << "abcd" << std::endl;

    //path p("/usr/local/include/boost/version.hpp");
    path p("./test.txt");
    cout << file_size(p) << endl;

    time_t t = last_write_time(p);
    last_write_time(p, time(0));

    (void)t;
}

//////////////////////////////////////////
#include <boost/ratio.hpp>
void case8()
{
    using namespace boost;
    //const int GBYTES = 1000*1000*1000;          //GB，不是GiB
    space_info si = space("/home/chrono");
    cout << si.capacity / giga::num<< endl;
    cout << si.available / giga::num<< endl;
    cout << si.free / giga::num<< endl;
}

//////////////////////////////////////////
void case9()
{
    //namespace fs = boost::filesystem;

    path ptest = "./test";
    if (exists(ptest))
    {
        if (fs::is_empty(ptest))
        {
            remove(ptest) ;
        }
        else
        {
            remove_all(ptest);
        }
    }

    assert(!exists(ptest));
    create_directory(ptest) ;

    copy_file("/usr/local/include/boost/version.hpp", ptest / "a.txt");
    assert(exists(ptest / "a.txt"));

    rename(ptest / "a.txt", ptest / "b.txt");
    assert(exists(ptest / "b.txt"));

    create_directories(ptest / "sub_dir1" / "sub_dir1");
}

//////////////////////////////////////////
void case10()
{
    //directory_iterator end;
    //for (directory_iterator pos("/usr/local/lib/");pos != end; ++pos)
    //{       cout << *pos << endl;   }
    string path = "/dev/shm";//"/tmp";

    typedef std::pair<directory_iterator, directory_iterator> dir_range;
    //dir_range dr(directory_iterator("/usr/local/lib/"),
    dir_range dr(directory_iterator(path.c_str()),
    directory_iterator());

    BOOST_FOREACH(auto& x , dr)
    {      cout << x << endl;       }

    typedef recursive_directory_iterator rd_iterator;

    rd_iterator  end;
    for (rd_iterator pos(path.c_str());pos != end; ++pos)
    {
        cout << "depth" << pos.depth() << ":" <<*pos << endl;
    }

}

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

int main()
{
    case1();
    case2();
    case3();
    case4();
    //case5();
    case6();
    case7();
    case8();
    //case9();
    case10();
}

<|endoftext|>


<commit_before>// Scintilla source code edit control
/** @file ExternalLexer.cxx
 ** Support external lexers in DLLs.
 **/
// Copyright 2001 Simon Steele <ss@pnotepad.org>, portions copyright Neil Hodgson.
// The License.txt file describes the conditions under which this software may be distributed.

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

#include "Platform.h"

#include "SciLexer.h"
#include "PropSet.h"
#include "Accessor.h"
#include "DocumentAccessor.h"
#include "KeyWords.h"
#include "ExternalLexer.h"

LexerManager *LexerManager::theInstance = NULL;

//------------------------------------------
//
// ExternalLexerModule
//
//------------------------------------------

char **WordListsToStrings(WordList *val[]) {
	int dim = 0;
	while (val[dim])
		dim++;
	char **wls = new char * [dim + 1];
	for (int i = 0;i < dim;i++) {
		SString words;
		words = "";
		for (int n = 0; n < val[i]->len; n++) {
			words += val[i]->words[n];
			if (n != val[i]->len - 1)
				words += " ";
		}
		wls[i] = new char[words.length() + 1];
		strcpy(wls[i], words.c_str());
	}
	wls[dim] = 0;
	return wls;
}

void DeleteWLStrings(char *strs[]) {
	int dim = 0;
	while (strs[dim]) {
		delete strs[dim];
		dim++;
	}
	delete [] strs;
}

void ExternalLexerModule::Lex(unsigned int startPos, int lengthDoc, int initStyle,
                              WordList *keywordlists[], Accessor &styler) const {
	if (!fneLexer)
		return ;

	char **kwds = WordListsToStrings(keywordlists);
	char *ps = styler.GetProperties();

	// The accessor passed in is always a DocumentAccessor so this cast and the subsequent
	// access will work. Can not use the stricter dynamic_cast as that requires RTTI.
	DocumentAccessor &da = static_cast<DocumentAccessor &>(styler);
	WindowID wID = da.GetWindow();

	fneLexer(externalLanguage, startPos, lengthDoc, initStyle, kwds, wID, ps);

	delete ps;
	DeleteWLStrings(kwds);
}

void ExternalLexerModule::Fold(unsigned int startPos, int lengthDoc, int initStyle,
                               WordList *keywordlists[], Accessor &styler) const {
	if (!fneFolder)
		return ;

	char **kwds = WordListsToStrings(keywordlists);
	char *ps = styler.GetProperties();

	// The accessor passed in is always a DocumentAccessor so this cast and the subsequent
	// access will work. Can not use the stricter dynamic_cast as that requires RTTI.
	DocumentAccessor &da = static_cast<DocumentAccessor &>(styler);
	WindowID wID = da.GetWindow();

	fneFolder(externalLanguage, startPos, lengthDoc, initStyle, kwds, wID, ps);

	delete ps;
	DeleteWLStrings(kwds);
}

void ExternalLexerModule::SetExternal(ExtLexerFunction fLexer, ExtFoldFunction fFolder, int index) {
	fneLexer = fLexer;
	fneFolder = fFolder;
	externalLanguage = index;
}

//------------------------------------------
//
// LexerLibrary
//
//------------------------------------------

LexerLibrary::LexerLibrary(const char* ModuleName) {
	// Initialise some members...
	first = NULL;
	last = NULL;

	// Load the DLL
	lib = DynamicLibrary::Load(ModuleName);
	if (lib->IsValid()) {
		m_sModuleName = ModuleName;
		//Cannot use reinterpret_cast because: ANSI C++ forbids casting between pointers to functions and objects
		GetLexerCountFn GetLexerCount = (GetLexerCountFn)lib->FindFunction("GetLexerCount");

		if (GetLexerCount) {
			ExternalLexerModule *lex;
			LexerMinder *lm;

			// Find functions in the DLL
			GetLexerNameFn GetLexerName = (GetLexerNameFn)lib->FindFunction("GetLexerName");
			ExtLexerFunction Lexer = (ExtLexerFunction)lib->FindFunction("Lex");
			ExtFoldFunction Folder = (ExtFoldFunction)lib->FindFunction("Fold");

			// Assign a buffer for the lexer name.
			char lexname[100];
			strcpy(lexname, "");

			int nl = GetLexerCount();

			for (int i = 0; i < nl; i++) {
				GetLexerName(i, lexname, 100);
				lex = new ExternalLexerModule(SCLEX_AUTOMATIC, NULL, lexname, NULL);

				// Create a LexerMinder so we don't leak the ExternalLexerModule...
				lm = new LexerMinder;
				lm->self = lex;
				lm->next = NULL;
				if (first != NULL) {
					last->next = lm;
					last = lm;
				} else {
					first = lm;
					last = lm;
				}

				// The external lexer needs to know how to call into its DLL to
				// do its lexing and folding, we tell it here. Folder may be null.
				lex->SetExternal(Lexer, Folder, i);
			}
		}
	}
	next = NULL;
}

LexerLibrary::~LexerLibrary() {
	Release();
	delete lib;
}

void LexerLibrary::Release() {
	//TODO maintain a list of lexers created, and delete them!
	LexerMinder *lm;
	LexerMinder *next;
	lm = first;
	while (NULL != lm) {
		next = lm->next;
		delete lm->self;
		delete lm;
		lm = next;
	}

	first = NULL;
	last = NULL;
}

//------------------------------------------
//
// LexerManager
//
//------------------------------------------

/// Return the single LexerManager instance...
LexerManager *LexerManager::GetInstance() {
	if(!theInstance)
		theInstance = new LexerManager;
	return theInstance;
}

/// Delete any LexerManager instance...
void LexerManager::DeleteInstance()
{
	if(theInstance) {
		delete theInstance;
		theInstance = NULL;
	}
}

/// protected constructor - this is a singleton...
LexerManager::LexerManager() {
	first = NULL;
	last = NULL;
}

LexerManager::~LexerManager() {
	Clear();
}

void LexerManager::Load(const char* path)
{
	LoadLexerLibrary(path);
}

void LexerManager::LoadLexerLibrary(const char* module)
{
	LexerLibrary *lib = new LexerLibrary(module);
	if (NULL != first) {
		last->next = lib;
		last = lib;
	} else {
		first = lib;
		last = lib;
	}
}

void LexerManager::Clear()
{
	if (NULL != first) {
		LexerLibrary *cur = first;
		LexerLibrary *next = first->next;
		while (cur) {
			delete cur;
			cur = next;
		}
		first = NULL;
		last = NULL;
	}
}

//------------------------------------------
//
// LexerManager
//
//------------------------------------------

LMMinder::~LMMinder()
{
	LexerManager::DeleteInstance();
}

LMMinder minder;<commit_msg>Added line end at end of file.<commit_after>// Scintilla source code edit control
/** @file ExternalLexer.cxx
 ** Support external lexers in DLLs.
 **/
// Copyright 2001 Simon Steele <ss@pnotepad.org>, portions copyright Neil Hodgson.
// The License.txt file describes the conditions under which this software may be distributed.

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

#include "Platform.h"

#include "SciLexer.h"
#include "PropSet.h"
#include "Accessor.h"
#include "DocumentAccessor.h"
#include "KeyWords.h"
#include "ExternalLexer.h"

LexerManager *LexerManager::theInstance = NULL;

//------------------------------------------
//
// ExternalLexerModule
//
//------------------------------------------

char **WordListsToStrings(WordList *val[]) {
	int dim = 0;
	while (val[dim])
		dim++;
	char **wls = new char * [dim + 1];
	for (int i = 0;i < dim;i++) {
		SString words;
		words = "";
		for (int n = 0; n < val[i]->len; n++) {
			words += val[i]->words[n];
			if (n != val[i]->len - 1)
				words += " ";
		}
		wls[i] = new char[words.length() + 1];
		strcpy(wls[i], words.c_str());
	}
	wls[dim] = 0;
	return wls;
}

void DeleteWLStrings(char *strs[]) {
	int dim = 0;
	while (strs[dim]) {
		delete strs[dim];
		dim++;
	}
	delete [] strs;
}

void ExternalLexerModule::Lex(unsigned int startPos, int lengthDoc, int initStyle,
                              WordList *keywordlists[], Accessor &styler) const {
	if (!fneLexer)
		return ;

	char **kwds = WordListsToStrings(keywordlists);
	char *ps = styler.GetProperties();

	// The accessor passed in is always a DocumentAccessor so this cast and the subsequent
	// access will work. Can not use the stricter dynamic_cast as that requires RTTI.
	DocumentAccessor &da = static_cast<DocumentAccessor &>(styler);
	WindowID wID = da.GetWindow();

	fneLexer(externalLanguage, startPos, lengthDoc, initStyle, kwds, wID, ps);

	delete ps;
	DeleteWLStrings(kwds);
}

void ExternalLexerModule::Fold(unsigned int startPos, int lengthDoc, int initStyle,
                               WordList *keywordlists[], Accessor &styler) const {
	if (!fneFolder)
		return ;

	char **kwds = WordListsToStrings(keywordlists);
	char *ps = styler.GetProperties();

	// The accessor passed in is always a DocumentAccessor so this cast and the subsequent
	// access will work. Can not use the stricter dynamic_cast as that requires RTTI.
	DocumentAccessor &da = static_cast<DocumentAccessor &>(styler);
	WindowID wID = da.GetWindow();

	fneFolder(externalLanguage, startPos, lengthDoc, initStyle, kwds, wID, ps);

	delete ps;
	DeleteWLStrings(kwds);
}

void ExternalLexerModule::SetExternal(ExtLexerFunction fLexer, ExtFoldFunction fFolder, int index) {
	fneLexer = fLexer;
	fneFolder = fFolder;
	externalLanguage = index;
}

//------------------------------------------
//
// LexerLibrary
//
//------------------------------------------

LexerLibrary::LexerLibrary(const char* ModuleName) {
	// Initialise some members...
	first = NULL;
	last = NULL;

	// Load the DLL
	lib = DynamicLibrary::Load(ModuleName);
	if (lib->IsValid()) {
		m_sModuleName = ModuleName;
		//Cannot use reinterpret_cast because: ANSI C++ forbids casting between pointers to functions and objects
		GetLexerCountFn GetLexerCount = (GetLexerCountFn)lib->FindFunction("GetLexerCount");

		if (GetLexerCount) {
			ExternalLexerModule *lex;
			LexerMinder *lm;

			// Find functions in the DLL
			GetLexerNameFn GetLexerName = (GetLexerNameFn)lib->FindFunction("GetLexerName");
			ExtLexerFunction Lexer = (ExtLexerFunction)lib->FindFunction("Lex");
			ExtFoldFunction Folder = (ExtFoldFunction)lib->FindFunction("Fold");

			// Assign a buffer for the lexer name.
			char lexname[100];
			strcpy(lexname, "");

			int nl = GetLexerCount();

			for (int i = 0; i < nl; i++) {
				GetLexerName(i, lexname, 100);
				lex = new ExternalLexerModule(SCLEX_AUTOMATIC, NULL, lexname, NULL);

				// Create a LexerMinder so we don't leak the ExternalLexerModule...
				lm = new LexerMinder;
				lm->self = lex;
				lm->next = NULL;
				if (first != NULL) {
					last->next = lm;
					last = lm;
				} else {
					first = lm;
					last = lm;
				}

				// The external lexer needs to know how to call into its DLL to
				// do its lexing and folding, we tell it here. Folder may be null.
				lex->SetExternal(Lexer, Folder, i);
			}
		}
	}
	next = NULL;
}

LexerLibrary::~LexerLibrary() {
	Release();
	delete lib;
}

void LexerLibrary::Release() {
	//TODO maintain a list of lexers created, and delete them!
	LexerMinder *lm;
	LexerMinder *next;
	lm = first;
	while (NULL != lm) {
		next = lm->next;
		delete lm->self;
		delete lm;
		lm = next;
	}

	first = NULL;
	last = NULL;
}

//------------------------------------------
//
// LexerManager
//
//------------------------------------------

/// Return the single LexerManager instance...
LexerManager *LexerManager::GetInstance() {
	if(!theInstance)
		theInstance = new LexerManager;
	return theInstance;
}

/// Delete any LexerManager instance...
void LexerManager::DeleteInstance()
{
	if(theInstance) {
		delete theInstance;
		theInstance = NULL;
	}
}

/// protected constructor - this is a singleton...
LexerManager::LexerManager() {
	first = NULL;
	last = NULL;
}

LexerManager::~LexerManager() {
	Clear();
}

void LexerManager::Load(const char* path)
{
	LoadLexerLibrary(path);
}

void LexerManager::LoadLexerLibrary(const char* module)
{
	LexerLibrary *lib = new LexerLibrary(module);
	if (NULL != first) {
		last->next = lib;
		last = lib;
	} else {
		first = lib;
		last = lib;
	}
}

void LexerManager::Clear()
{
	if (NULL != first) {
		LexerLibrary *cur = first;
		LexerLibrary *next = first->next;
		while (cur) {
			delete cur;
			cur = next;
		}
		first = NULL;
		last = NULL;
	}
}

//------------------------------------------
//
// LexerManager
//
//------------------------------------------

LMMinder::~LMMinder()
{
	LexerManager::DeleteInstance();
}

LMMinder minder;
<|endoftext|>


<commit_before>/***********************************************************************************************************************
 **
 ** Copyright (c) 2011, 2013 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 "VComment.h"
#include "VCommentBrowser.h"
#include "VCommentImage.h"
#include "VisualizationBase/src/items/Line.h"
#include "VisualizationBase/src/items/ItemStyle.h"
#include "VisualizationBase/src/items/Text.h"
#include "VisualizationBase/src/declarative/DeclarativeItemDef.h"

using namespace Visualization;

namespace Comments {

ITEM_COMMON_DEFINITIONS(VComment, "item")

VComment::VComment(Item* parent, NodeType* node) : Super(parent, node, itemStyles().get())
{
	// import existing diagrams
	for(auto diagram : *node->diagrams())
	{
		diagrams_[diagram->name()] = diagram;
	}

	editing_ = node->lines()->size() == 0 || (node->lines()->size() == 1 && node->lines()->at(0)->get().isEmpty());
	parseLines();
}

// split up user-provided text into single elements
void VComment::parseLines()
{
	clearChildren();
	bool isHTML = false;

	QSet<QString> diagramNames{};
	int listCount = -1;

	for(auto nodeLine : *node()->lines())
	{
		QRegExp rx("^={3,}|-{3,}|\\.{3,}$");
		QString line = nodeLine->get();

		// is this a new enumeration item?
		if(line.left(3) == " * ")
		{
			listCount++;
			// does this create a new list?
			if(listCount == 0)
			{
				pushTextLine("

");
				line = line.mid(3);
			}
			// otherwise, just add another list item
			else
			{
				pushTextLine("

");
				line = line.mid(3);
			}
		}
		// or is this extending an existing list?
		else if(line.left(3) == "   " && listCount > -1)
		{
			line = line.mid(3);
		}
		// if this is not an enumeration item, reset listCount
		else if(listCount > -1 && line.left(3) != " * " && line.left(3) != "   ")
		{
			pushTextLine("

");
			listCount = -1;
		}

		// is this HTML?
		if(line == "")
		{
			popLineBuffer();
			isHTML = true;
			continue;
		}
		else if(isHTML)
		{
			if(line == "")
			{
				isHTML = false;
				popLineBuffer(true);
			}
			else
			{
				pushTextLine(line);
			}
			// don't process further
			continue;
		}

		if(rx.exactMatch(line))
		{
			// A line consists of one of . - = that is repeated three times or more
			// The used character defines the strength of the header, i.e. one of three levels
			QString style;
			switch(line[0].toAscii())
			{
				default:
				case '.': style = "single"; break;
				case '-': style = "double"; break;
				case '=': style = "triple"; break;
			}

			addChildItem(new Line(this, Line::itemStyles().get(style)));
			continue;
		}

		// is this a header? replace it right away with the appropriate tag
		rx.setPattern("^(#+)([^#].*)");
		// allow headers h1 to h6
		if(rx.exactMatch(line) && rx.cap(1).length() <= 6)
		{
			QString len = QString::number(rx.cap(1).length());
			pushTextLine("<h" + len + ">" + rx.cap(2).simplified() + "</h" + len + ">");
		}
		// is this a diagram? format: [diagram#diagramName]
		else if(line.left(9) == "[diagram#" && line.right(1) == "]" && line.size() > 9+1)
		{
			QString diagramName = line.mid(9,line.size()-9-1);
			diagramNames << diagramName;

			CommentDiagram* diagram = diagrams_.value(diagramName, nullptr);

			if(diagram == nullptr)
			{
				diagram = new CommentDiagram(nullptr, diagramName);
				diagrams_[diagramName] = diagram;
			}

			auto item = renderer()->render(this, diagram);
			addChildItem(item);
		}
		// urls are specified as [browser#http://www.google.com]
		else if(line.left(9) == "[browser#" && line.right(1) == "]" && line.size() > 9+1)
		{
			QString mid = line.mid(9, line.size()-9-1);
			// read width and height, if specified
			auto items = parseMarkdownArguments(mid);
			QString url = items->at(0).second;
			auto browser = new VCommentBrowser(this, QUrl(url));

			if(items->size() > 1)
			{
				QSize size = parseSize(items->at(1).second);
				browser->updateSize(size);
			}

			addChildItem(browser);
			delete items;
		}
		// images are specified as
		//   [image#/home/user/image.png]
		//   [image#image.png|300x300] to specify a size
		else if(line.left(7) == "[image#" && line.right(1) == "]" && line.size() > 7+1)
		{
			QString mid = line.mid(7, line.size()-7-1);
			// read width and height, if specified
			auto items = parseMarkdownArguments(mid);
			QString path = items->at(0).second;
			QSize size(0,0);
			if(items->size() > 1)
				size = parseSize(items->at(1).second);

			addChildItem(new VCommentImage(this, items->at(0).second, size));
			delete items;
		}
		else
		{
			pushTextLine(line);
		}
	}

	popLineBuffer();
	synchroniseDiagrams(diagramNames);
}

void VComment::synchroniseDiagrams(QSet<QString> itemDiagramNames)
{
	// get all diagrams from the node
	auto nodeDiagrams = node()->diagrams();
	// gather all names from the node diagrams for easier comparison
	QSet<QString> nodeDiagramNames{};
	for(auto diagram : *nodeDiagrams)
		nodeDiagramNames << diagram->name();

	// get intersection of two sets
	QSet<QString> intersection(itemDiagramNames);
	intersection.intersect(nodeDiagramNames);

	// new diagrams were already constructed inside of parseLines(),
	// they also need to be added to the model now
	auto newDiagramNames = itemDiagramNames - intersection;

	if(newDiagramNames.size() > 0)
	{
		node()->model()->beginModification(node(), "Adding new diagrams");
		for(auto diagramName : newDiagramNames)
		{
			auto diagram = diagrams_.value(diagramName);
			node()->diagrams()->append(diagram);
		}
		node()->model()->endModification();
	}

	// diagrams that are no longer referenced need to be removed from the model
	auto oldDiagramNames = nodeDiagramNames - intersection;

	if(oldDiagramNames.size() > 0)
	{
		node()->model()->beginModification(node(), "Removing unreferenced diagrams");
		for(auto diagramName : oldDiagramNames)
		{
			auto diagram = diagrams_.value(diagramName);
			node()->diagrams()->remove(diagram);
			diagrams_.remove(diagramName);
		}
		node()->model()->endModification();
	}
}

QMap<QString, CommentDiagram*> VComment::diagrams() const
{
	return diagrams_;
}

QSize VComment::parseSize(const QString& str)
{
	int index = str.indexOf('x');
	bool ok{};

	int width = str.left(index).toInt(&ok);
	if(index > 0 && !ok)
		qDebug() << "Invalid width specified in size string:" << str;

	int height = str.mid(index+1).toInt(&ok);
	if(index+1 < str.size()-1 && !ok)
		qDebug() << "Invalid height specified in size string:" << str;

	return QSize(width, height);
}

QVector<QPair<QString,QString>>* VComment::parseMarkdownArguments(const QString& argString)
{
	// split string on all pipes
	auto lines = argString.split('|');
	// TODO: get rid of escaped pipes \| e.g. in case an url contains one

	auto pairs = new QVector<QPair<QString,QString>>();
	// read key/value pairs
	QRegExp rx("^[a-zA-Z]{,15}=");
	for(auto line : lines)
	{
		int index = rx.indexIn(line);
		if(index == -1)
			pairs->push_back(qMakePair(QString(), line));
		else
			pairs->push_back(qMakePair(line.left(index), line.mid(index+1)));
	}

	return pairs;
}

QString VComment::replaceMarkdown(QString str)
{
	QRegExp rx;

	rx.setPattern("\\*\\*([^\\*]+)\\*\\*");
	str.replace(rx, "\\1");

	rx.setPattern("\\*([^\\*]+)\\*");
	str.replace(rx, "\\1");

	return str;
}

void VComment::pushTextLine(QString text)
{
	lineBuffer_.push_back(text);
}

void VComment::popLineBuffer(bool asHtml)
{
	if(lineBuffer_.size() > 0)
	{
		auto joined = lineBuffer_.join("\n");

		if(asHtml)
		{
			auto browser = new VCommentBrowser(this, joined);
			children_.push_back(browser);
		}
		else
		{
			auto text = new Text(this, Text::itemStyles().get("comment"), replaceMarkdown(joined));
			text->setTextFormat(Qt::RichText);
			children_.push_back(text);
		}

		lineBuffer_.clear();
	}
}

void VComment::addChildItem(Visualization::Item* item)
{
	popLineBuffer();
	children_.push_back(item);
}

void VComment::toggleEditing()
{
	if(node()->lines()->size() == 0)
		editing_ = true;
	else
		editing_ = !editing_;

	if(!editing_)
		parseLines();

	setUpdateNeeded(StandardUpdate);
}

bool VComment::editing() const
{
	return editing_;
}

void VComment::initializeForms()
{
	addForm((new SequentialLayoutFormElement())
				->setVertical()
				->setListOfItems([](Item* i) {
					auto vc = static_cast<VComment*>(i);
					return vc->children_;
				}
	));

	addForm(item<Visualization::VList>(&I::editLabel_, [](I* v){ return v->node()->lines(); },
			[](I* v){return &v->style()->editList();}));
}

int VComment::determineForm()
{
	return editing_ ? 1 : 0;
}

} /* namespace Comments */
<commit_msg>Add our own bullets.<commit_after>/***********************************************************************************************************************
 **
 ** Copyright (c) 2011, 2013 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 "VComment.h"
#include "VCommentBrowser.h"
#include "VCommentImage.h"
#include "VisualizationBase/src/items/Line.h"
#include "VisualizationBase/src/items/ItemStyle.h"
#include "VisualizationBase/src/items/Text.h"
#include "VisualizationBase/src/declarative/DeclarativeItemDef.h"

using namespace Visualization;

namespace Comments {

ITEM_COMMON_DEFINITIONS(VComment, "item")

VComment::VComment(Item* parent, NodeType* node) : Super(parent, node, itemStyles().get())
{
	// import existing diagrams
	for(auto diagram : *node->diagrams())
	{
		diagrams_[diagram->name()] = diagram;
	}

	editing_ = node->lines()->size() == 0 || (node->lines()->size() == 1 && node->lines()->at(0)->get().isEmpty());
	parseLines();
}

// split up user-provided text into single elements
void VComment::parseLines()
{
	clearChildren();
	bool isHTML = false;

	QSet<QString> diagramNames{};
	int listCount = -1;

	for(auto nodeLine : *node()->lines())
	{
		QRegExp rx("^={3,}|-{3,}|\\.{3,}$");
		QString line = nodeLine->get();

		// is this a new enumeration item?
		if(line.left(3) == " * ")
		{
			listCount++;
			// does this create a new list?
			if(listCount == 0)
			{
				pushTextLine("

• ");
				line = line.mid(3);
			}
			// otherwise, just add another list item
			else
			{
				pushTextLine("

• ");
				line = line.mid(3);
			}
		}
		// or is this extending an existing list?
		else if(line.left(3) == "   " && listCount > -1)
		{
			line = line.mid(3);
		}
		// if this is not an enumeration item, reset listCount
		else if(listCount > -1 && line.left(3) != " * " && line.left(3) != "   ")
		{
			pushTextLine("

");
			listCount = -1;
		}

		// is this HTML?
		if(line == "")
		{
			popLineBuffer();
			isHTML = true;
			continue;
		}
		else if(isHTML)
		{
			if(line == "")
			{
				isHTML = false;
				popLineBuffer(true);
			}
			else
			{
				pushTextLine(line);
			}
			// don't process further
			continue;
		}

		if(rx.exactMatch(line))
		{
			// A line consists of one of . - = that is repeated three times or more
			// The used character defines the strength of the header, i.e. one of three levels
			QString style;
			switch(line[0].toAscii())
			{
				default:
				case '.': style = "single"; break;
				case '-': style = "double"; break;
				case '=': style = "triple"; break;
			}

			addChildItem(new Line(this, Line::itemStyles().get(style)));
			continue;
		}

		// is this a header? replace it right away with the appropriate tag
		rx.setPattern("^(#+)([^#].*)");
		// allow headers h1 to h6
		if(rx.exactMatch(line) && rx.cap(1).length() <= 6)
		{
			QString len = QString::number(rx.cap(1).length());
			pushTextLine("<h" + len + ">" + rx.cap(2).simplified() + "</h" + len + ">");
		}
		// is this a diagram? format: [diagram#diagramName]
		else if(line.left(9) == "[diagram#" && line.right(1) == "]" && line.size() > 9+1)
		{
			QString diagramName = line.mid(9,line.size()-9-1);
			diagramNames << diagramName;

			CommentDiagram* diagram = diagrams_.value(diagramName, nullptr);

			if(diagram == nullptr)
			{
				diagram = new CommentDiagram(nullptr, diagramName);
				diagrams_[diagramName] = diagram;
			}

			auto item = renderer()->render(this, diagram);
			addChildItem(item);
		}
		// urls are specified as [browser#http://www.google.com]
		else if(line.left(9) == "[browser#" && line.right(1) == "]" && line.size() > 9+1)
		{
			QString mid = line.mid(9, line.size()-9-1);
			// read width and height, if specified
			auto items = parseMarkdownArguments(mid);
			QString url = items->at(0).second;
			auto browser = new VCommentBrowser(this, QUrl(url));

			if(items->size() > 1)
			{
				QSize size = parseSize(items->at(1).second);
				browser->updateSize(size);
			}

			addChildItem(browser);
			delete items;
		}
		// images are specified as
		//   [image#/home/user/image.png]
		//   [image#image.png|300x300] to specify a size
		else if(line.left(7) == "[image#" && line.right(1) == "]" && line.size() > 7+1)
		{
			QString mid = line.mid(7, line.size()-7-1);
			// read width and height, if specified
			auto items = parseMarkdownArguments(mid);
			QString path = items->at(0).second;
			QSize size(0,0);
			if(items->size() > 1)
				size = parseSize(items->at(1).second);

			addChildItem(new VCommentImage(this, items->at(0).second, size));
			delete items;
		}
		else
		{
			pushTextLine(line);
		}
	}

	popLineBuffer();
	synchroniseDiagrams(diagramNames);
}

void VComment::synchroniseDiagrams(QSet<QString> itemDiagramNames)
{
	// get all diagrams from the node
	auto nodeDiagrams = node()->diagrams();
	// gather all names from the node diagrams for easier comparison
	QSet<QString> nodeDiagramNames{};
	for(auto diagram : *nodeDiagrams)
		nodeDiagramNames << diagram->name();

	// get intersection of two sets
	QSet<QString> intersection(itemDiagramNames);
	intersection.intersect(nodeDiagramNames);

	// new diagrams were already constructed inside of parseLines(),
	// they also need to be added to the model now
	auto newDiagramNames = itemDiagramNames - intersection;

	if(newDiagramNames.size() > 0)
	{
		node()->model()->beginModification(node(), "Adding new diagrams");
		for(auto diagramName : newDiagramNames)
		{
			auto diagram = diagrams_.value(diagramName);
			node()->diagrams()->append(diagram);
		}
		node()->model()->endModification();
	}

	// diagrams that are no longer referenced need to be removed from the model
	auto oldDiagramNames = nodeDiagramNames - intersection;

	if(oldDiagramNames.size() > 0)
	{
		node()->model()->beginModification(node(), "Removing unreferenced diagrams");
		for(auto diagramName : oldDiagramNames)
		{
			auto diagram = diagrams_.value(diagramName);
			node()->diagrams()->remove(diagram);
			diagrams_.remove(diagramName);
		}
		node()->model()->endModification();
	}
}

QMap<QString, CommentDiagram*> VComment::diagrams() const
{
	return diagrams_;
}

QSize VComment::parseSize(const QString& str)
{
	int index = str.indexOf('x');
	bool ok{};

	int width = str.left(index).toInt(&ok);
	if(index > 0 && !ok)
		qDebug() << "Invalid width specified in size string:" << str;

	int height = str.mid(index+1).toInt(&ok);
	if(index+1 < str.size()-1 && !ok)
		qDebug() << "Invalid height specified in size string:" << str;

	return QSize(width, height);
}

QVector<QPair<QString,QString>>* VComment::parseMarkdownArguments(const QString& argString)
{
	// split string on all pipes
	auto lines = argString.split('|');
	// TODO: get rid of escaped pipes \| e.g. in case an url contains one

	auto pairs = new QVector<QPair<QString,QString>>();
	// read key/value pairs
	QRegExp rx("^[a-zA-Z]{,15}=");
	for(auto line : lines)
	{
		int index = rx.indexIn(line);
		if(index == -1)
			pairs->push_back(qMakePair(QString(), line));
		else
			pairs->push_back(qMakePair(line.left(index), line.mid(index+1)));
	}

	return pairs;
}

QString VComment::replaceMarkdown(QString str)
{
	QRegExp rx;

	rx.setPattern("\\*\\*([^\\*]+)\\*\\*");
	str.replace(rx, "\\1");

	rx.setPattern("\\*([^\\*]+)\\*");
	str.replace(rx, "\\1");

	return str;
}

void VComment::pushTextLine(QString text)
{
	lineBuffer_.push_back(text);
}

void VComment::popLineBuffer(bool asHtml)
{
	if(lineBuffer_.size() > 0)
	{
		auto joined = lineBuffer_.join("\n");

		if(asHtml)
		{
			auto browser = new VCommentBrowser(this, joined);
			children_.push_back(browser);
		}
		else
		{
			auto text = new Text(this, Text::itemStyles().get("comment"), replaceMarkdown(joined));
			text->setTextFormat(Qt::RichText);
			children_.push_back(text);
		}

		lineBuffer_.clear();
	}
}

void VComment::addChildItem(Visualization::Item* item)
{
	popLineBuffer();
	children_.push_back(item);
}

void VComment::toggleEditing()
{
	if(node()->lines()->size() == 0)
		editing_ = true;
	else
		editing_ = !editing_;

	if(!editing_)
		parseLines();

	setUpdateNeeded(StandardUpdate);
}

bool VComment::editing() const
{
	return editing_;
}

void VComment::initializeForms()
{
	addForm((new SequentialLayoutFormElement())
				->setVertical()
				->setListOfItems([](Item* i) {
					auto vc = static_cast<VComment*>(i);
					return vc->children_;
				}
	));

	addForm(item<Visualization::VList>(&I::editLabel_, [](I* v){ return v->node()->lines(); },
			[](I* v){return &v->style()->editList();}));
}

int VComment::determineForm()
{
	return editing_ ? 1 : 0;
}

} /* namespace Comments */
<|endoftext|>


<commit_before>/****************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the Qt Mobility Components.
**
** $QT_BEGIN_LICENSE:BSD$
** You may use this file under the terms of the BSD license as follows:
**
** "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 Nokia Corporation and its Subsidiary(-ies) 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
** OWNER 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."
** $QT_END_LICENSE$
**
****************************************************************************/

#include "browser.h"

#include "albumview.h"
#include "artistview.h"
#include "photoview.h"
#include "songview.h"

#include "qdocumentgallery.h"

#include <QtGui>

Browser::Browser(QWidget *parent, Qt::WindowFlags flags)
    : QMainWindow(parent, flags)
    , gallery(0)
    , stack(0)
    , artistView(0)
    , albumArtistView(0)
    , albumView(0)
    , songView(0)
    , photoView(0)
{
    gallery = new QDocumentGallery;

    artistView = new ArtistView(QDocumentGallery::Artist, gallery);
    connect(artistView, SIGNAL(showAlbums(QVariant,QString)),
            this, SLOT(showAlbums(QVariant,QString)));
    connect(artistView, SIGNAL(showSongs(QVariant,QString)),
            this, SLOT(showSongs(QVariant,QString)));

    albumArtistView = new ArtistView(QDocumentGallery::AlbumArtist, gallery);
    connect(albumArtistView, SIGNAL(showAlbums(QVariant,QString)),
            this, SLOT(showAlbums(QVariant,QString)));
    connect(albumArtistView, SIGNAL(showSongs(QVariant,QString)),
            this, SLOT(showSongs(QVariant,QString)));

    albumView = new AlbumView(gallery);
    connect(albumView, SIGNAL(showSongs(QVariant,QString)),
            this, SLOT(showSongs(QVariant,QString)));

    songView = new SongView(gallery);

    photoView = new PhotoView(gallery);

    stack = new QStackedWidget;
    stack->addWidget(artistView);
    stack->addWidget(albumArtistView);
    stack->addWidget(albumView);
    stack->addWidget(songView);
    stack->addWidget(photoView);

    menuBar()->addAction(tr("Artists"), this, SLOT(showArtists()));
    menuBar()->addAction(tr("Album Artists"), this, SLOT(showAlbumArtists()));
    menuBar()->addAction(tr("Albums"), this, SLOT(showAlbums()));
    menuBar()->addAction(tr("Songs"), this, SLOT(showSongs()));
    menuBar()->addAction(tr("Photos"), this, SLOT(showPhotos()));

    setCentralWidget(stack);
    showArtists();

#ifdef Q_WS_MAEMO_5
    setAttribute(Qt::WA_Maemo5StackedWindow);
#endif
}

Browser::~Browser()
{
}

void Browser::showArtists()
{
    showView(artistView, tr("Artists"));
}

void Browser::showArtists(const QVariant &containerId, const QString &title)
{
#ifdef Q_WS_MAEMO_5
    ArtistView *artistView = new ArtistView(QDocumentGallery::Artist, gallery);
    connect(artistView, SIGNAL(showAlbums(QVariant,QString)),
            this, SLOT(showAlbums(QVariant,QString)));
    connect(artistView, SIGNAL(showSongs(QVariant,QString)),
            this, SLOT(showSongs(QVariant,QString)));
#endif

    showView(artistView, containerId, title);
}

void Browser::showAlbumArtists()
{
    showView(albumArtistView, tr("Album Artists"));
}

void Browser::showAlbumArtists(const QVariant &containerId, const QString &title)
{
#ifdef Q_WS_MAEMO_5
    ArtistView *albumArtistView = new ArtistView(QDocumentGallery::AlbumArtist, gallery);
    connect(albumArtistView, SIGNAL(showAlbums(QVariant,QString)),
            this, SLOT(showAlbums(QVariant,QString)));
    connect(albumArtistView, SIGNAL(showSongs(QVariant,QString)),
            this, SLOT(showSongs(QVariant,QString)));
#endif

    showView(albumArtistView, containerId, title);
}

void Browser::showAlbums()
{
    showView(albumView, tr("Albums"));
}

void Browser::showAlbums(const QVariant &containerId, const QString &title)
{
#ifdef Q_WS_MAEMO_5
    AlbumView *albumView = new AlbumView(gallery);
    connect(albumView, SIGNAL(showSongs(QVariant,QString)),
            this, SLOT(showSongs(QVariant,QString)));
#endif

    showView(albumView, containerId, title);
}

void Browser::showSongs()
{
    showView(songView, tr("Songs"));
}

void Browser::showSongs(const QVariant &containerId, const QString &title)
{
#ifdef Q_WS_MAEMO_5
    SongView *songView = new SongView(gallery);
#endif

    showView(songView, containerId, title);
}

void Browser::showPhotos()
{
    showView(photoView, tr("Photos"));
}

void Browser::showPhotos(const QVariant &containerId, const QString &title)
{
#ifdef Q_WS_MAEMO_5
    PhotoView *photoView = new PhotoView(gallery);
#endif

    showView(photoView, containerId, title);
}

void Browser::showView(GalleryView *view, const QString &title)
{
    view->showChildren(QVariant());

    stack->setCurrentWidget(view);

    setWindowTitle(title);
}

void Browser::showView(GalleryView *view, const QVariant &containerId, const QString &title)
{
    view->showChildren(containerId);

#ifdef Q_WS_MAEMO_5
    QWidget *parent = qobject_cast<QWidget *>(sender());
    if (parent)
        view->setParent(parent->window(), Qt::Window);
    view->setAttribute(Qt::WA_Maemo5StackedWindow);
    view->setAttribute(Qt::WA_DeleteOnClose);
    view->setWindowTitle(title);
    view->show();
#else
    stack->setCurrentWidget(view);

    setWindowTitle(title);
#endif
}
<commit_msg>Don't call showArtists() twice on startup in the media browser.<commit_after>/****************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the Qt Mobility Components.
**
** $QT_BEGIN_LICENSE:BSD$
** You may use this file under the terms of the BSD license as follows:
**
** "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 Nokia Corporation and its Subsidiary(-ies) 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
** OWNER 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."
** $QT_END_LICENSE$
**
****************************************************************************/

#include "browser.h"

#include "albumview.h"
#include "artistview.h"
#include "photoview.h"
#include "songview.h"

#include "qdocumentgallery.h"

#include <QtGui>

Browser::Browser(QWidget *parent, Qt::WindowFlags flags)
    : QMainWindow(parent, flags)
    , gallery(0)
    , stack(0)
    , artistView(0)
    , albumArtistView(0)
    , albumView(0)
    , songView(0)
    , photoView(0)
{
    gallery = new QDocumentGallery;

    artistView = new ArtistView(QDocumentGallery::Artist, gallery);
    connect(artistView, SIGNAL(showAlbums(QVariant,QString)),
            this, SLOT(showAlbums(QVariant,QString)));
    connect(artistView, SIGNAL(showSongs(QVariant,QString)),
            this, SLOT(showSongs(QVariant,QString)));

    albumArtistView = new ArtistView(QDocumentGallery::AlbumArtist, gallery);
    connect(albumArtistView, SIGNAL(showAlbums(QVariant,QString)),
            this, SLOT(showAlbums(QVariant,QString)));
    connect(albumArtistView, SIGNAL(showSongs(QVariant,QString)),
            this, SLOT(showSongs(QVariant,QString)));

    albumView = new AlbumView(gallery);
    connect(albumView, SIGNAL(showSongs(QVariant,QString)),
            this, SLOT(showSongs(QVariant,QString)));

    songView = new SongView(gallery);

    photoView = new PhotoView(gallery);

    stack = new QStackedWidget;
    stack->addWidget(artistView);
    stack->addWidget(albumArtistView);
    stack->addWidget(albumView);
    stack->addWidget(songView);
    stack->addWidget(photoView);

    menuBar()->addAction(tr("Artists"), this, SLOT(showArtists()));
    menuBar()->addAction(tr("Album Artists"), this, SLOT(showAlbumArtists()));
    menuBar()->addAction(tr("Albums"), this, SLOT(showAlbums()));
    menuBar()->addAction(tr("Songs"), this, SLOT(showSongs()));
    menuBar()->addAction(tr("Photos"), this, SLOT(showPhotos()));

    setCentralWidget(stack);

#ifdef Q_WS_MAEMO_5
    setAttribute(Qt::WA_Maemo5StackedWindow);
#endif
}

Browser::~Browser()
{
}

void Browser::showArtists()
{
    showView(artistView, tr("Artists"));
}

void Browser::showArtists(const QVariant &containerId, const QString &title)
{
#ifdef Q_WS_MAEMO_5
    ArtistView *artistView = new ArtistView(QDocumentGallery::Artist, gallery);
    connect(artistView, SIGNAL(showAlbums(QVariant,QString)),
            this, SLOT(showAlbums(QVariant,QString)));
    connect(artistView, SIGNAL(showSongs(QVariant,QString)),
            this, SLOT(showSongs(QVariant,QString)));
#endif

    showView(artistView, containerId, title);
}

void Browser::showAlbumArtists()
{
    showView(albumArtistView, tr("Album Artists"));
}

void Browser::showAlbumArtists(const QVariant &containerId, const QString &title)
{
#ifdef Q_WS_MAEMO_5
    ArtistView *albumArtistView = new ArtistView(QDocumentGallery::AlbumArtist, gallery);
    connect(albumArtistView, SIGNAL(showAlbums(QVariant,QString)),
            this, SLOT(showAlbums(QVariant,QString)));
    connect(albumArtistView, SIGNAL(showSongs(QVariant,QString)),
            this, SLOT(showSongs(QVariant,QString)));
#endif

    showView(albumArtistView, containerId, title);
}

void Browser::showAlbums()
{
    showView(albumView, tr("Albums"));
}

void Browser::showAlbums(const QVariant &containerId, const QString &title)
{
#ifdef Q_WS_MAEMO_5
    AlbumView *albumView = new AlbumView(gallery);
    connect(albumView, SIGNAL(showSongs(QVariant,QString)),
            this, SLOT(showSongs(QVariant,QString)));
#endif

    showView(albumView, containerId, title);
}

void Browser::showSongs()
{
    showView(songView, tr("Songs"));
}

void Browser::showSongs(const QVariant &containerId, const QString &title)
{
#ifdef Q_WS_MAEMO_5
    SongView *songView = new SongView(gallery);
#endif

    showView(songView, containerId, title);
}

void Browser::showPhotos()
{
    showView(photoView, tr("Photos"));
}

void Browser::showPhotos(const QVariant &containerId, const QString &title)
{
#ifdef Q_WS_MAEMO_5
    PhotoView *photoView = new PhotoView(gallery);
#endif

    showView(photoView, containerId, title);
}

void Browser::showView(GalleryView *view, const QString &title)
{
    view->showChildren(QVariant());

    stack->setCurrentWidget(view);

    setWindowTitle(title);
}

void Browser::showView(GalleryView *view, const QVariant &containerId, const QString &title)
{
    view->showChildren(containerId);

#ifdef Q_WS_MAEMO_5
    QWidget *parent = qobject_cast<QWidget *>(sender());
    if (parent)
        view->setParent(parent->window(), Qt::Window);
    view->setAttribute(Qt::WA_Maemo5StackedWindow);
    view->setAttribute(Qt::WA_DeleteOnClose);
    view->setWindowTitle(title);
    view->show();
#else
    stack->setCurrentWidget(view);

    setWindowTitle(title);
#endif
}
<|endoftext|>


<commit_before>/*
 * Copyright 2005 Sandia Corporation.
 * Under the terms of Contract DE-AC04-94AL85000, there is a non-exclusive
 * license for use of this work by or on behalf of the
 * U.S. Government. Redistribution and use in source and binary forms, with
 * or without modification, are permitted provided that this Notice and any
 * statement of authorship are reproduced on all copies.
 */
#include "vtkMath.h"
#include "vtkMathConfigure.h"

#include <vtkstd/limits>

#ifndef ABS
#define ABS(x) ((x) < 0 ? -(x) : (x))
#endif

template<class A>
bool fuzzyCompare(A a, A b)
{
  return ABS(a - b) < vtkstd::numeric_limits<A>::epsilon();
}

template<class A>
bool fuzzyCompare(A a[3], A b[3])
{
  return fuzzyCompare(a[0], b[0]) &&
         fuzzyCompare(a[1], b[1]) &&
         fuzzyCompare(a[2], b[2]);
}

//=============================================================================
// Helpful class for storing and using color triples.
class Triple {
public:
  Triple() {};
  Triple(double a, double b, double c) {
    data[0] = a; data[1] = b; data[2] = c;
  }
  const double *operator()() const { return data; }
  double *operator()() { return data; }
  const double &operator[](int i) const { return data[i]; }
  double &operator[](int i) { return data[i]; }
  bool operator==(const Triple &triple) const {
    return *this == triple.data;
  }
  bool operator==(const double *triple) const {
    return (   (this->data[0] - triple[0] <= 0.01*ABS(data[0])+0.02)
            && (this->data[0] - triple[0] >= -0.01*ABS(data[0])-0.02)
            && (this->data[1] - triple[1] <= 0.01*ABS(data[1])+0.02)
            && (this->data[1] - triple[1] >= -0.01*ABS(data[1])-0.02)
            && (this->data[2] - triple[2] <= 0.01*ABS(data[2])+0.02)
            && (this->data[2] - triple[2] >= -0.01*ABS(data[2])-0.02) );
  }
  bool operator!=(const Triple &triple) const {
    return *this != triple.data;
  }
  bool operator!=(const double *triple) const {
    return !(*this == triple);
  }
private:
  double data[3];
};

static ostream &operator<<(ostream &os, const Triple t)
{
  os << t[0] << ", " << t[1] << ", " << t[2];
  return os;
}

//=============================================================================
// Function for comparing colors.  Each value should be equivalent in the
// respective color space.
static int TestColorConvert(const Triple &rgb, const Triple &hsv,
                            const Triple &xyz, const Triple &lab);

// Function for comparing special doubles like Inf and NaN.
#define TestSpecialDoubles(value, inftest, nantest) \
  TestSpecialDoublesReal(value, #value, inftest, nantest)
static int TestSpecialDoublesReal(double value, const char *name,
                                  bool inftest, bool nantest);

int TestMath(int,char *[])
{
  int testIntValue;

  testIntValue = vtkMath::Factorial(5);
  if ( testIntValue != 120 )
    {
    vtkGenericWarningMacro("Factorial(5) = "<<testIntValue<<" != 120");
    return 1;
    }

  testIntValue = vtkMath::Binomial(8,3);
  if ( testIntValue != 56 )
    {
    vtkGenericWarningMacro("Binomial(8,3) = "<<testIntValue<<" != 56");
    return 1;
    }

  testIntValue = vtkMath::Binomial(5,3);
  if ( testIntValue != 10 )
    {
    vtkGenericWarningMacro("Binomial(5,3) = "<<testIntValue<<" != 10");
    return 1;
    }

  // Test add, subtract, scalar multiplication.
  double a[3] = {1.0, 2.0, 3.0};
  double b[3] = {0.0, 1.0, 2.0};
  double c[3];
  double ans1[3] = {1.0, 3.0, 5.0};
  double ans2[3] = {1.0, 1.0, 1.0};
  double ans3[3] = {3.0, 6.0, 9.0};
  float af[3] = {1.0f, 2.0f, 3.0f};
  float bf[3] = {0.0f, 1.0f, 2.0f};
  float cf[3];
  float ans1f[3] = {1.0, 3.0, 5.0};
  float ans2f[3] = {1.0, 1.0, 1.0};
  float ans3f[3] = {3.0, 6.0, 9.0};

  vtkMath::Add(a, b, c);
  if (!fuzzyCompare(c, ans1))
    {
    vtkGenericWarningMacro("Double addition failed.");
    return 1;
    }
  vtkMath::Subtract(a, b, c);
  if (!fuzzyCompare(c, ans2))
    {
    vtkGenericWarningMacro("Double subtraction failed.");
    return 1;
    }
  vtkMath::MultiplyScalar(a, 3.0);
  if (!fuzzyCompare(a, ans3))
    {
    vtkGenericWarningMacro("Double scalar multiplication failed.");
    return 1;
    }
  vtkMath::Add(af, bf, cf);
  if (!fuzzyCompare(cf, ans1f))
    {
    vtkGenericWarningMacro("Float addition failed.");
    return 1;
    }
  vtkMath::Subtract(af, bf, cf);
  if (!fuzzyCompare(cf, ans2f))
    {
    vtkGenericWarningMacro("Float subtraction failed.");
    return 1;
    }
  vtkMath::MultiplyScalar(af, 3.0f);
  if (!fuzzyCompare(af, ans3f))
    {
    vtkGenericWarningMacro("Float scalar multiplication failed.");
    return 1;
    }

  // Test color conversion.
  int colorsPassed = 1;

  colorsPassed &= TestColorConvert(Triple(1.0, 1.0, 1.0),             // RGB
                                   Triple(0.0, 0.0, 1.0),   // HSV (H ambiguous)
                                   Triple(0.9505, 1.000, 1.089),      // XYZ
                                   Triple(100.0, 0.0, 0.0));          // CIELAB

  colorsPassed &= TestColorConvert(Triple(0.5, 0.5, 0.0),             // RGB
                                   Triple(1.0/6.0, 1.0, 0.5),         // HSV
                                   Triple(0.165, 0.199, 0.030),       // XYZ
                                   Triple(51.7, -12.90, 56.54));      // CIELAB

  colorsPassed &= TestColorConvert(Triple(0.25, 0.25, 0.5),           // RGB
                                   Triple(2.0/3.0, 0.5, 0.5),         // HSV
                                   Triple(0.078, 0.063, 0.211),       // XYZ
                                   Triple(30.11, 18.49, -36.18));     // CIELAB

  colorsPassed &= TestColorConvert(Triple(0.0, 0.0, 0.0),             // RGB
                                   Triple(0.0, 0.0, 0.0), // HSV (H&S ambiguous)
                                   Triple(0.0, 0.0, 0.0),             // XYZ
                                   Triple(0.0, 0.0, 0.0));            // CIELAB

  if (!colorsPassed)
    {
    return 1;
    }

  if (!TestSpecialDoubles(0, false, false)) return 1;
  if (!TestSpecialDoubles(5, false, false)) return 1;
  if (!TestSpecialDoubles(vtkMath::Inf(), true, false)) return 1;
  if (!TestSpecialDoubles(vtkMath::NegInf(), true, false)) return 1;
  if (!TestSpecialDoubles(vtkMath::Nan(), false, true)) return 1;

  if (!(0 < vtkMath::Inf()))
    {
    vtkGenericWarningMacro(<< "Odd comparison for infinity.");
    return 1;
    }
  if (!(0 > vtkMath::NegInf()))
    {
    vtkGenericWarningMacro(<< "Odd comparison for negitive infinity.");
    return 1;
    }

  return 0;
}

static int TestColorConvert(const Triple &rgb, const Triple &hsv,
                            const Triple &xyz, const Triple &lab)
{
  cout << "Ensuring the following colors are consistent: " << endl;
  cout << "   RGB:      " << rgb << endl;
  cout << "   HSV:      " << hsv << endl;
  cout << "   CIE XYZ:  " << xyz << endl;
  cout << "   CIE-L*ab: " << lab << endl;

  Triple result1;
  double *result2;

#define COMPARE(testname, target, dest) \
  if (target != dest)                              \
    { \
    vtkGenericWarningMacro(<< "Incorrect " #testname " conversion.  Got " \
                           << dest << " expected " << target); \
    return 0; \
    }

  // Test conversion between RGB and HSV.
  vtkMath::RGBToHSV(rgb(), result1());
  COMPARE(RGBToHSV, hsv, result1);
  vtkMath::HSVToRGB(hsv(), result1());
  COMPARE(HSVToRGB, rgb, result1);

  result2 = vtkMath::RGBToHSV(rgb());
  COMPARE(RGBToHSV, hsv, result2);
  result2 = vtkMath::HSVToRGB(hsv());
  COMPARE(HSVToRGB, rgb, result2);

  vtkMath::RGBToHSV(rgb[0], rgb[1], rgb[2],
                    &result1[0], &result1[1], &result1[2]);
  COMPARE(RGBToHSV, hsv, result1);
  vtkMath::HSVToRGB(hsv[0], hsv[1], hsv[2],
                    &result1[0], &result1[1], &result1[2]);
  COMPARE(HSVToRGB, rgb, result1);

  // Test conversion between RGB and XYZ.
  vtkMath::RGBToXYZ(rgb(), result1());
  COMPARE(RGBToXYZ, xyz, result1);
  vtkMath::XYZToRGB(xyz(), result1());
  COMPARE(XYZToRGB, rgb, result1);

  result2 = vtkMath::RGBToXYZ(rgb());
  COMPARE(RGBToXYZ, xyz, result2);
  result2 = vtkMath::XYZToRGB(xyz());
  COMPARE(XYZToRGB, rgb, result2);

  vtkMath::RGBToXYZ(rgb[0], rgb[1], rgb[2],
                    &result1[0], &result1[1], &result1[2]);
  COMPARE(RGBToXYZ, xyz, result1);
  vtkMath::XYZToRGB(xyz[0], xyz[1], xyz[2],
                    &result1[0], &result1[1], &result1[2]);
  COMPARE(XYZToRGB, rgb, result1);

  // Test conversion between Lab and XYZ.
  vtkMath::LabToXYZ(lab(), result1());
  COMPARE(LabToXYZ, xyz, result1);
  vtkMath::XYZToLab(xyz(), result1());
  COMPARE(XYZToLab, lab, result1);

  result2 = vtkMath::LabToXYZ(lab());
  COMPARE(LabToXYZ, xyz, result2);
  result2 = vtkMath::XYZToLab(xyz());
  COMPARE(XYZToLab, lab, result2);

  vtkMath::LabToXYZ(lab[0], lab[1], lab[2],
                    &result1[0], &result1[1], &result1[2]);
  COMPARE(LabToXYZ, xyz, result1);
  vtkMath::XYZToLab(xyz[0], xyz[1], xyz[2],
                    &result1[0], &result1[1], &result1[2]);
  COMPARE(XYZToLab, lab, result1);

  // Test conversion between Lab and RGB.
  vtkMath::LabToRGB(lab(), result1());
  COMPARE(LabToRGB, rgb, result1);
  vtkMath::RGBToLab(rgb(), result1());
  COMPARE(RGBToLab, lab, result1);

  result2 = vtkMath::LabToRGB(lab());
  COMPARE(LabToRGB, rgb, result2);
  result2 = vtkMath::RGBToLab(rgb());
  COMPARE(RGBToLab, lab, result2);

  vtkMath::LabToRGB(lab[0], lab[1], lab[2],
                    &result1[0], &result1[1], &result1[2]);
  COMPARE(LabToRGB, rgb, result1);
  vtkMath::RGBToLab(rgb[0], rgb[1], rgb[2],
                    &result1[0], &result1[1], &result1[2]);
  COMPARE(RGBToLab, lab, result1);

  return 1;
}

static int TestSpecialDoublesReal(double value, const char *name,
                                  bool inftest, bool nantest)
{
  cout << "Testing comparison of " << name << " to non-finite values." << endl;
  cout << "  * IsNan test." << endl;
  if (vtkMath::IsNan(value) != static_cast<int>(nantest))
    {
    cout << value << " failed the IsNan test." << endl;
    return 0;
    }
  cout << "  * IsInf test." << endl;
  if (vtkMath::IsInf(value) != static_cast<int>(inftest))
    {
    cout << value << " failed the IsInf test." << endl;
    return 0;
    }
  cout << "  * Tests passed." << endl;

  return 1;
}
<commit_msg>BUG: Print out subtraction result so we can see what the error is.<commit_after>/*
 * Copyright 2005 Sandia Corporation.
 * Under the terms of Contract DE-AC04-94AL85000, there is a non-exclusive
 * license for use of this work by or on behalf of the
 * U.S. Government. Redistribution and use in source and binary forms, with
 * or without modification, are permitted provided that this Notice and any
 * statement of authorship are reproduced on all copies.
 */
#include "vtkMath.h"
#include "vtkMathConfigure.h"

#include <vtkstd/limits>

#ifndef ABS
#define ABS(x) ((x) < 0 ? -(x) : (x))
#endif

template<class A>
bool fuzzyCompare(A a, A b)
{
  return ABS(a - b) < vtkstd::numeric_limits<A>::epsilon();
}

template<class A>
bool fuzzyCompare(A a[3], A b[3])
{
  return fuzzyCompare(a[0], b[0]) &&
         fuzzyCompare(a[1], b[1]) &&
         fuzzyCompare(a[2], b[2]);
}

//=============================================================================
// Helpful class for storing and using color triples.
class Triple {
public:
  Triple() {};
  Triple(double a, double b, double c) {
    data[0] = a; data[1] = b; data[2] = c;
  }
  const double *operator()() const { return data; }
  double *operator()() { return data; }
  const double &operator[](int i) const { return data[i]; }
  double &operator[](int i) { return data[i]; }
  bool operator==(const Triple &triple) const {
    return *this == triple.data;
  }
  bool operator==(const double *triple) const {
    return (   (this->data[0] - triple[0] <= 0.01*ABS(data[0])+0.02)
            && (this->data[0] - triple[0] >= -0.01*ABS(data[0])-0.02)
            && (this->data[1] - triple[1] <= 0.01*ABS(data[1])+0.02)
            && (this->data[1] - triple[1] >= -0.01*ABS(data[1])-0.02)
            && (this->data[2] - triple[2] <= 0.01*ABS(data[2])+0.02)
            && (this->data[2] - triple[2] >= -0.01*ABS(data[2])-0.02) );
  }
  bool operator!=(const Triple &triple) const {
    return *this != triple.data;
  }
  bool operator!=(const double *triple) const {
    return !(*this == triple);
  }
private:
  double data[3];
};

static ostream &operator<<(ostream &os, const Triple t)
{
  os << t[0] << ", " << t[1] << ", " << t[2];
  return os;
}

//=============================================================================
// Function for comparing colors.  Each value should be equivalent in the
// respective color space.
static int TestColorConvert(const Triple &rgb, const Triple &hsv,
                            const Triple &xyz, const Triple &lab);

// Function for comparing special doubles like Inf and NaN.
#define TestSpecialDoubles(value, inftest, nantest) \
  TestSpecialDoublesReal(value, #value, inftest, nantest)
static int TestSpecialDoublesReal(double value, const char *name,
                                  bool inftest, bool nantest);

int TestMath(int,char *[])
{
  int testIntValue;

  testIntValue = vtkMath::Factorial(5);
  if ( testIntValue != 120 )
    {
    vtkGenericWarningMacro("Factorial(5) = "<<testIntValue<<" != 120");
    return 1;
    }

  testIntValue = vtkMath::Binomial(8,3);
  if ( testIntValue != 56 )
    {
    vtkGenericWarningMacro("Binomial(8,3) = "<<testIntValue<<" != 56");
    return 1;
    }

  testIntValue = vtkMath::Binomial(5,3);
  if ( testIntValue != 10 )
    {
    vtkGenericWarningMacro("Binomial(5,3) = "<<testIntValue<<" != 10");
    return 1;
    }

  // Test add, subtract, scalar multiplication.
  double a[3] = {1.0, 2.0, 3.0};
  double b[3] = {0.0, 1.0, 2.0};
  double c[3];
  double ans1[3] = {1.0, 3.0, 5.0};
  double ans2[3] = {1.0, 1.0, 1.0};
  double ans3[3] = {3.0, 6.0, 9.0};
  float af[3] = {1.0f, 2.0f, 3.0f};
  float bf[3] = {0.0f, 1.0f, 2.0f};
  float cf[3];
  float ans1f[3] = {1.0, 3.0, 5.0};
  float ans2f[3] = {1.0, 1.0, 1.0};
  float ans3f[3] = {3.0, 6.0, 9.0};

  vtkMath::Add(a, b, c);
  if (!fuzzyCompare(c, ans1))
    {
    vtkGenericWarningMacro("Double addition failed.");
    return 1;
    }
  vtkMath::Subtract(a, b, c);
  if (!fuzzyCompare(c, ans2))
    {
    vtkGenericWarningMacro("Double subtraction failed.");
    return 1;
    }
  vtkMath::MultiplyScalar(a, 3.0);
  if (!fuzzyCompare(a, ans3))
    {
    vtkGenericWarningMacro("Double scalar multiplication failed.");
    return 1;
    }
  vtkMath::Add(af, bf, cf);
  if (!fuzzyCompare(cf, ans1f))
    {
    vtkGenericWarningMacro("Float addition failed.");
	cout << "Result: { " << cf[0] << ", " << cf[1] << ", " << cf[2] << " }" << endl;
    return 1;
    }
  vtkMath::Subtract(af, bf, cf);
  if (!fuzzyCompare(cf, ans2f))
    {
    vtkGenericWarningMacro("Float subtraction failed.");
    return 1;
    }
  vtkMath::MultiplyScalar(af, 3.0f);
  if (!fuzzyCompare(af, ans3f))
    {
    vtkGenericWarningMacro("Float scalar multiplication failed.");
    return 1;
    }

  // Test color conversion.
  int colorsPassed = 1;

  colorsPassed &= TestColorConvert(Triple(1.0, 1.0, 1.0),             // RGB
                                   Triple(0.0, 0.0, 1.0),   // HSV (H ambiguous)
                                   Triple(0.9505, 1.000, 1.089),      // XYZ
                                   Triple(100.0, 0.0, 0.0));          // CIELAB

  colorsPassed &= TestColorConvert(Triple(0.5, 0.5, 0.0),             // RGB
                                   Triple(1.0/6.0, 1.0, 0.5),         // HSV
                                   Triple(0.165, 0.199, 0.030),       // XYZ
                                   Triple(51.7, -12.90, 56.54));      // CIELAB

  colorsPassed &= TestColorConvert(Triple(0.25, 0.25, 0.5),           // RGB
                                   Triple(2.0/3.0, 0.5, 0.5),         // HSV
                                   Triple(0.078, 0.063, 0.211),       // XYZ
                                   Triple(30.11, 18.49, -36.18));     // CIELAB

  colorsPassed &= TestColorConvert(Triple(0.0, 0.0, 0.0),             // RGB
                                   Triple(0.0, 0.0, 0.0), // HSV (H&S ambiguous)
                                   Triple(0.0, 0.0, 0.0),             // XYZ
                                   Triple(0.0, 0.0, 0.0));            // CIELAB

  if (!colorsPassed)
    {
    return 1;
    }

  if (!TestSpecialDoubles(0, false, false)) return 1;
  if (!TestSpecialDoubles(5, false, false)) return 1;
  if (!TestSpecialDoubles(vtkMath::Inf(), true, false)) return 1;
  if (!TestSpecialDoubles(vtkMath::NegInf(), true, false)) return 1;
  if (!TestSpecialDoubles(vtkMath::Nan(), false, true)) return 1;

  if (!(0 < vtkMath::Inf()))
    {
    vtkGenericWarningMacro(<< "Odd comparison for infinity.");
    return 1;
    }
  if (!(0 > vtkMath::NegInf()))
    {
    vtkGenericWarningMacro(<< "Odd comparison for negitive infinity.");
    return 1;
    }

  return 0;
}

static int TestColorConvert(const Triple &rgb, const Triple &hsv,
                            const Triple &xyz, const Triple &lab)
{
  cout << "Ensuring the following colors are consistent: " << endl;
  cout << "   RGB:      " << rgb << endl;
  cout << "   HSV:      " << hsv << endl;
  cout << "   CIE XYZ:  " << xyz << endl;
  cout << "   CIE-L*ab: " << lab << endl;

  Triple result1;
  double *result2;

#define COMPARE(testname, target, dest) \
  if (target != dest)                              \
    { \
    vtkGenericWarningMacro(<< "Incorrect " #testname " conversion.  Got " \
                           << dest << " expected " << target); \
    return 0; \
    }

  // Test conversion between RGB and HSV.
  vtkMath::RGBToHSV(rgb(), result1());
  COMPARE(RGBToHSV, hsv, result1);
  vtkMath::HSVToRGB(hsv(), result1());
  COMPARE(HSVToRGB, rgb, result1);

  result2 = vtkMath::RGBToHSV(rgb());
  COMPARE(RGBToHSV, hsv, result2);
  result2 = vtkMath::HSVToRGB(hsv());
  COMPARE(HSVToRGB, rgb, result2);

  vtkMath::RGBToHSV(rgb[0], rgb[1], rgb[2],
                    &result1[0], &result1[1], &result1[2]);
  COMPARE(RGBToHSV, hsv, result1);
  vtkMath::HSVToRGB(hsv[0], hsv[1], hsv[2],
                    &result1[0], &result1[1], &result1[2]);
  COMPARE(HSVToRGB, rgb, result1);

  // Test conversion between RGB and XYZ.
  vtkMath::RGBToXYZ(rgb(), result1());
  COMPARE(RGBToXYZ, xyz, result1);
  vtkMath::XYZToRGB(xyz(), result1());
  COMPARE(XYZToRGB, rgb, result1);

  result2 = vtkMath::RGBToXYZ(rgb());
  COMPARE(RGBToXYZ, xyz, result2);
  result2 = vtkMath::XYZToRGB(xyz());
  COMPARE(XYZToRGB, rgb, result2);

  vtkMath::RGBToXYZ(rgb[0], rgb[1], rgb[2],
                    &result1[0], &result1[1], &result1[2]);
  COMPARE(RGBToXYZ, xyz, result1);
  vtkMath::XYZToRGB(xyz[0], xyz[1], xyz[2],
                    &result1[0], &result1[1], &result1[2]);
  COMPARE(XYZToRGB, rgb, result1);

  // Test conversion between Lab and XYZ.
  vtkMath::LabToXYZ(lab(), result1());
  COMPARE(LabToXYZ, xyz, result1);
  vtkMath::XYZToLab(xyz(), result1());
  COMPARE(XYZToLab, lab, result1);

  result2 = vtkMath::LabToXYZ(lab());
  COMPARE(LabToXYZ, xyz, result2);
  result2 = vtkMath::XYZToLab(xyz());
  COMPARE(XYZToLab, lab, result2);

  vtkMath::LabToXYZ(lab[0], lab[1], lab[2],
                    &result1[0], &result1[1], &result1[2]);
  COMPARE(LabToXYZ, xyz, result1);
  vtkMath::XYZToLab(xyz[0], xyz[1], xyz[2],
                    &result1[0], &result1[1], &result1[2]);
  COMPARE(XYZToLab, lab, result1);

  // Test conversion between Lab and RGB.
  vtkMath::LabToRGB(lab(), result1());
  COMPARE(LabToRGB, rgb, result1);
  vtkMath::RGBToLab(rgb(), result1());
  COMPARE(RGBToLab, lab, result1);

  result2 = vtkMath::LabToRGB(lab());
  COMPARE(LabToRGB, rgb, result2);
  result2 = vtkMath::RGBToLab(rgb());
  COMPARE(RGBToLab, lab, result2);

  vtkMath::LabToRGB(lab[0], lab[1], lab[2],
                    &result1[0], &result1[1], &result1[2]);
  COMPARE(LabToRGB, rgb, result1);
  vtkMath::RGBToLab(rgb[0], rgb[1], rgb[2],
                    &result1[0], &result1[1], &result1[2]);
  COMPARE(RGBToLab, lab, result1);

  return 1;
}

static int TestSpecialDoublesReal(double value, const char *name,
                                  bool inftest, bool nantest)
{
  cout << "Testing comparison of " << name << " to non-finite values." << endl;
  cout << "  * IsNan test." << endl;
  if (vtkMath::IsNan(value) != static_cast<int>(nantest))
    {
    cout << value << " failed the IsNan test." << endl;
    return 0;
    }
  cout << "  * IsInf test." << endl;
  if (vtkMath::IsInf(value) != static_cast<int>(inftest))
    {
    cout << value << " failed the IsInf test." << endl;
    return 0;
    }
  cout << "  * Tests passed." << endl;

  return 1;
}
<|endoftext|>


<commit_before>// vim:filetype=cpp:textwidth=120:shiftwidth=2:softtabstop=2:expandtab
// Copyright 2014 Christoph Schwering

#include <gtest/gtest.h>

#include <iostream>

#include <limbo/formula.h>

namespace limbo {

using Sort = Language::Sort;
using Var  = Language::Var;
using Name = Language::Name;
using Func = Language::Func;
using Symbol = Language::Symbol;
using Word = Language::Word;

std::ostream& operator<<(std::ostream& os, const Symbol& s) {
  switch (s.type) {
    case Symbol::kFunc:      os << 'f' << s.u.f.index(); break;
    case Symbol::kName:      os << 'n' << s.u.n.index(); break;
    case Symbol::kVar:       os << 'x' << s.u.x.index(); break;
    case Symbol::kTerm:      os << 't'; break;
    case Symbol::kEquals:    os << "\u003D"; break;
    case Symbol::kNotEquals: os << "\u2260"; break;
    case Symbol::kLiteral:   os << 'l'; break;
    case Symbol::kClause:    os << 'c'; break;
    case Symbol::kNot:       os << "\u2227 "; break;
    case Symbol::kExists:    os << "\u2203 x" << s.u.x.index(); break;
    case Symbol::kForall:    os << "\u2200 x" << s.u.x.index(); break;
    case Symbol::kOr:        os << "\u2228"; break;
    case Symbol::kAnd:       os << "\u2227"; break;
    case Symbol::kKnow:      os << "know_" << s.u.k; break;
    case Symbol::kMaybe:     os << "maybe_" << s.u.k; break;
    case Symbol::kBelieve:   os << "bel_" << s.u.k << ',' << s.u.l; break;
    case Symbol::kAction:    os << "A "; break;
  }
  return os;
}

std::ostream& operator<<(std::ostream& os, const Word& w) {
  for (const Symbol& s : w) {
    os << s << ' ';
  }
  return os;
}

std::ostream& operator<<(std::ostream& os, const Formula::Reader& r) {
  switch (r.type()) {
    case Symbol::kFunc:
    case Symbol::kVar:
    case Symbol::kName: {
      os << r.head();
      for (int i = 0; i < r.arity(); ++i) {
        if (i == 0) {
          os << '(';
        }
        os << r.arg(i);
        if (i+1 < r.arity()) {
          os << ',';
        } else {
          os << ')';
        }
      }
      break;
    }
    case Symbol::kEquals:
    case Symbol::kNotEquals: {
      os << r.arg(0) << ' ' << r.head() << ' ' << r.arg(1);
      break;
    }
    case Symbol::kTerm:
    case Symbol::kLiteral:
    case Symbol::kClause:
      os << r.head();
      break;
    case Symbol::kNot:
    case Symbol::kExists:
    case Symbol::kForall:
    case Symbol::kKnow:
    case Symbol::kMaybe:
      os << r.head() << ' ' << r.arg(0);
      break;
    case Symbol::kBelieve:
      os << r.head() << ' ' << r.arg(0) << " \u27FE " << r.arg(1);
      break;
    case Symbol::kOr:
    case Symbol::kAnd:
      os << (r.type() == Symbol::kOr ? '[' : '(');
      for (int i = 0; i < r.arity(); ++i) {
        os << r.arg(i);
        if (i+1 < r.arity()) {
          os << ' ' << r.head() << ' ';
        }
      }
      os << (r.type() == Symbol::kOr ? ']' : ')');
      break;
    case Symbol::kAction:
      os << '[' << r.arg(0) << ']' << ' ' << r.arg(1);
      break;
  }
  return os;
}

std::ostream& operator<<(std::ostream& os, const Formula& f) {
  os << f.reader();
  return os;
}

TEST(FormulaTest, Rectify) {
  Language* l = Language::Instance();
  Sort s = l->CreateSort(false);
  Var x = l->CreateVar(s);
  Var y = l->CreateVar(s);
  Var z = l->CreateVar(s);
  Var u = l->CreateVar(s);
  Name n = l->CreateName(s, 0);
  Func c = l->CreateFunc(s, 0);
  Func f = l->CreateFunc(s, 2);
  Func g = l->CreateFunc(s, 1);
  Word fxy = Word::Func(f, std::vector<Word>{Word::Var(x), Word::Var(y)});
  Word fyz = Word::Func(f, std::vector<Word>{Word::Var(y), Word::Var(z)});
  Word gfxy = Word::Func(g, std::vector<Word>{fxy});
  Word gfyz = Word::Func(g, std::vector<Word>{fyz});
  Word w = Word::Exists(x, Word::Or(Word::Forall(y, Word::Exists(z, Word::Equals(fxy.Clone(), fyz.Clone()))),
                                    Word::Exists(x, Word::Forall(y, Word::Exists(z, Word::Exists(u, Word::Equals(gfxy.Clone(), gfyz.Clone())))))));

  // Skolemize
  // Swearize
  // Rectify
  // Flatten
  // PushInwards

  {
    std::cout << "" << std::endl;
    Formula phi(Word::Exists(x, Word::Equals(Word::Func(c, std::vector<Word>{}), Word::Name(n, std::vector<Word>{}))));
    std::cout << "Orig: " << phi << std::endl;
    phi.Rectify();
    std::cout << "Rect: " << phi << std::endl;
    phi.Skolemize();
    std::cout << "Skol: " << phi << std::endl;
    phi.PushInwards();
    std::cout << "Push: " << phi << std::endl;
  }

  {
    std::cout << "" << std::endl;
    Formula phi(w.Clone());
    std::cout << "Orig: " << phi << std::endl;
    phi.Rectify();
    std::cout << "Rect: " << phi << std::endl;
    phi.Flatten();
    std::cout << "Flat: " << phi << std::endl;
    phi.PushInwards();
    std::cout << "Push: " << phi << std::endl;
  }
}

}  // namespace limbo

<commit_msg>Formula reorganisation.<commit_after>// vim:filetype=cpp:textwidth=120:shiftwidth=2:softtabstop=2:expandtab
// Copyright 2014 Christoph Schwering

#include <gtest/gtest.h>

#include <iostream>

#include <limbo/formula.h>

namespace limbo {

using Abc = Alphabet;
using Symbol = Abc::Symbol;
using Word = Abc::Word;
using F = Formula;

std::ostream& operator<<(std::ostream& os, const Symbol& s) {
  switch (s.tag) {
    case Symbol::kFunc:      os << 'f' << s.u.f.index(); break;
    case Symbol::kName:      os << 'n' << s.u.n.index(); break;
    case Symbol::kVar:       os << 'x' << s.u.x.index(); break;
    case Symbol::kTerm:      os << 't'; break;
    case Symbol::kEquals:    os << "\u003D"; break;
    case Symbol::kNotEquals: os << "\u2260"; break;
    case Symbol::kLiteral:   os << 'l'; break;
    case Symbol::kClause:    os << 'c'; break;
    case Symbol::kNot:       os << "\u2227 "; break;
    case Symbol::kExists:    os << "\u2203 x" << s.u.x.index(); break;
    case Symbol::kForall:    os << "\u2200 x" << s.u.x.index(); break;
    case Symbol::kOr:        os << "\u2228"; break;
    case Symbol::kAnd:       os << "\u2227"; break;
    case Symbol::kKnow:      os << "know_" << s.u.k; break;
    case Symbol::kMaybe:     os << "maybe_" << s.u.k; break;
    case Symbol::kBelieve:   os << "bel_" << s.u.k << ',' << s.u.l; break;
    case Symbol::kAction:    os << "A "; break;
  }
  return os;
}

std::ostream& operator<<(std::ostream& os, const Word& w) {
  for (const Symbol& s : w) {
    os << s << ' ';
  }
  return os;
}

std::ostream& operator<<(std::ostream& os, const RFormula& r) {
  switch (r.tag()) {
    case Symbol::kFunc:
    case Symbol::kVar:
    case Symbol::kName: {
      os << r.head();
      for (int i = 0; i < r.arity(); ++i) {
        if (i == 0) {
          os << '(';
        }
        os << r.arg(i);
        if (i+1 < r.arity()) {
          os << ',';
        } else {
          os << ')';
        }
      }
      break;
    }
    case Symbol::kEquals:
    case Symbol::kNotEquals: {
      os << r.arg(0) << ' ' << r.head() << ' ' << r.arg(1);
      break;
    }
    case Symbol::kTerm:
    case Symbol::kLiteral:
    case Symbol::kClause:
      os << r.head();
      break;
    case Symbol::kNot:
    case Symbol::kExists:
    case Symbol::kForall:
    case Symbol::kKnow:
    case Symbol::kMaybe:
      os << r.head() << ' ' << r.arg(0);
      break;
    case Symbol::kBelieve:
      os << r.head() << ' ' << r.arg(0) << " \u27FE " << r.arg(1);
      break;
    case Symbol::kOr:
    case Symbol::kAnd:
      os << (r.tag() == Symbol::kOr ? '[' : '(');
      for (int i = 0; i < r.arity(); ++i) {
        os << r.arg(i);
        if (i+1 < r.arity()) {
          os << ' ' << r.head() << ' ';
        }
      }
      os << (r.tag() == Symbol::kOr ? ']' : ')');
      break;
    case Symbol::kAction:
      os << '[' << r.arg(0) << ']' << ' ' << r.arg(1);
      break;
  }
  return os;
}

std::ostream& operator<<(std::ostream& os, const F& f) {
  os << f.readable();
  return os;
}

TEST(FormulaTest, Rectify) {
  Alphabet* abc = Alphabet::Instance();
  Abc::Sort s = abc->CreateSort(false);
  Abc::Var x = abc->CreateVar(s);
  Abc::Var y = abc->CreateVar(s);
  Abc::Var z = abc->CreateVar(s);
  Abc::Var u = abc->CreateVar(s);
  Abc::Name n = abc->CreateName(s, 0);
  Abc::Func c = abc->CreateFunc(s, 0);
  Abc::Func f = abc->CreateFunc(s, 2);
  Abc::Func g = abc->CreateFunc(s, 1);
  F fxy = F::Func(f, std::vector<F>{F::Var(x), F::Var(y)});
  F fyz = F::Func(f, std::vector<F>{F::Var(y), F::Var(z)});
  F gfxy = F::Func(g, std::vector<F>{fxy});
  F gfyz = F::Func(g, std::vector<F>{fyz});
  F w = F::Exists(x, F::Or(F::Forall(y, F::Exists(z, F::Equals(fxy, fyz))),
                                    F::Exists(x, F::Forall(y, F::Exists(z, F::Exists(u, F::Equals(gfxy, gfyz)))))));

  // Skolemize
  // Swearize
  // Rectify
  // Flatten
  // PushInwards

  {
    std::cout << "" << std::endl;
    F phi(F::Exists(x, F::Equals(F::Func(c, std::vector<F>{}), F::Name(n, std::vector<F>{}))));
    std::cout << "Orig: " << phi << std::endl;
    phi.Rectify();
    std::cout << "Rect: " << phi << std::endl;
    phi.Skolemize();
    std::cout << "Skol: " << phi << std::endl;
    phi.PushInwards();
    std::cout << "Push: " << phi << std::endl;
  }

  {
    std::cout << "" << std::endl;
    F phi(w);
    std::cout << "Orig: " << phi << std::endl;
    phi.Rectify();
    std::cout << "Rect: " << phi << std::endl;
    phi.Flatten();
    std::cout << "Flat: " << phi << std::endl;
    phi.PushInwards();
    std::cout << "Push: " << phi << std::endl;
  }
}

}  // namespace limbo

<|endoftext|>


<commit_before>/**
 *  Copyright (c) 2014-present, Facebook, Inc.
 *  All rights reserved.
 *
 *  This source code is licensed in accordance with the terms specified in
 *  the LICENSE file found in the root directory of this source tree.
 */

#include <set>

#include <osquery/config/config.h>
#include <osquery/database.h>
#include <osquery/logger.h>
#include <osquery/registry_factory.h>
#include <osquery/sql.h>

namespace osquery {

/**
 * @brief A simple ConfigParserPlugin for a "views" dictionary key.
 */
class ViewsConfigParserPlugin : public ConfigParserPlugin {
 public:
  std::vector<std::string> keys() const override {
    return {"views"};
  }

  Status update(const std::string& source, const ParserConfig& config) override;

 private:
  const std::string kConfigViews = "config_views.";
};

Status ViewsConfigParserPlugin::update(const std::string& source,
                                       const ParserConfig& config) {
  auto cv = config.find("views");
  if (cv == config.end()) {
    return Status(1);
  }

  auto obj = data_.getObject();
  data_.copyFrom(cv->second.doc(), obj);
  data_.add("views", obj);

  const auto& views = data_.doc()["views"];

  // We use a restricted scope below to change the data structure from
  // an array to a set. This lets us do deletes much more efficiently
  std::vector<std::string> created_views;
  std::set<std::string> erase_views;
  {
    std::vector<std::string> old_views_vec;
    scanDatabaseKeys(kQueries, old_views_vec, kConfigViews);
    for (const auto& view : old_views_vec) {
      erase_views.insert(view.substr(kConfigViews.size()));
    }
  }

  QueryData r;
  if (views.IsObject()) {
    for (const auto& view : views.GetObject()) {
      std::string name = view.name.GetString();
      if (!view.value.IsString()) {
        continue;
      }
      std::string query = view.value.GetString();
      if (query.empty()) {
        continue;
      }

      std::string old_query = "";
      getDatabaseValue(kQueries, kConfigViews + name, old_query);
      erase_views.erase(name);
      if (old_query == query) {
        continue;
      }

      // View has been updated
      osquery::query("DROP VIEW " + name, r);
      auto s = osquery::query("CREATE VIEW " + name + " AS " + query, r);
      if (s.ok()) {
        setDatabaseValue(kQueries, kConfigViews + name, query);
      } else {
        LOG(INFO) << "Error creating view (" << name << "): " << s.getMessage();
      }
    }
  }

  // Any views left are views that don't exist in the new configuration file
  // so we tear them down and remove them from the database.
  for (const auto& old_view : erase_views) {
    osquery::query("DROP VIEW " + old_view, r);
    deleteDatabaseValue(kQueries, kConfigViews + old_view);
  }
  return Status(0, "OK");
}

REGISTER_INTERNAL(ViewsConfigParserPlugin, "config_parser", "views");
}
<commit_msg>Ensure views are recreated on startup. (#5732)<commit_after>/**
 *  Copyright (c) 2014-present, Facebook, Inc.
 *  All rights reserved.
 *
 *  This source code is licensed in accordance with the terms specified in
 *  the LICENSE file found in the root directory of this source tree.
 */

#include <set>

#include <osquery/config/config.h>
#include <osquery/database.h>
#include <osquery/logger.h>
#include <osquery/registry_factory.h>
#include <osquery/sql.h>

namespace osquery {

/**
 * @brief A simple ConfigParserPlugin for a "views" dictionary key.
 */
class ViewsConfigParserPlugin : public ConfigParserPlugin {
 public:
  std::vector<std::string> keys() const override {
    return {"views"};
  }

  Status update(const std::string& source, const ParserConfig& config) override;

 private:
  const std::string kConfigViews = "config_views.";
  std::atomic<bool> first_time_{true};
};

Status ViewsConfigParserPlugin::update(const std::string& source,
                                       const ParserConfig& config) {
  auto cv = config.find("views");
  if (cv == config.end()) {
    return Status(1);
  }

  auto obj = data_.getObject();
  data_.copyFrom(cv->second.doc(), obj);
  data_.add("views", obj);

  const auto& views = data_.doc()["views"];

  // We use a restricted scope below to change the data structure from
  // an array to a set. This lets us do deletes much more efficiently
  std::vector<std::string> created_views;
  std::set<std::string> erase_views;
  {
    std::vector<std::string> old_views_vec;
    scanDatabaseKeys(kQueries, old_views_vec, kConfigViews);
    for (const auto& view : old_views_vec) {
      erase_views.insert(view.substr(kConfigViews.size()));
    }
  }

  QueryData r;
  if (views.IsObject()) {
    for (const auto& view : views.GetObject()) {
      std::string name = view.name.GetString();
      if (!view.value.IsString()) {
        continue;
      }
      std::string query = view.value.GetString();
      if (query.empty()) {
        continue;
      }

      std::string old_query = "";
      getDatabaseValue(kQueries, kConfigViews + name, old_query);
      erase_views.erase(name);

      // If query exists in the store, view would already have been
      // created and we don't need to create it. Except, at startup,
      // the view always needs to be created.
      if (!first_time_ && old_query == query) {
        continue;
      }

      // View has been updated
      osquery::query("DROP VIEW " + name, r);
      auto s = osquery::query("CREATE VIEW " + name + " AS " + query, r);
      if (s.ok()) {
        setDatabaseValue(kQueries, kConfigViews + name, query);
      } else {
        LOG(INFO) << "Error creating view (" << name << "): " << s.getMessage();
      }
    }
  }

  // Any views left are views that don't exist in the new configuration file
  // so we tear them down and remove them from the database.
  for (const auto& old_view : erase_views) {
    osquery::query("DROP VIEW " + old_view, r);
    deleteDatabaseValue(kQueries, kConfigViews + old_view);
  }

  first_time_ = false;
  return Status(0, "OK");
}

REGISTER_INTERNAL(ViewsConfigParserPlugin, "config_parser", "views");
}
<|endoftext|>