<commit_before>
// -*- mode: C++ -*-
// -*- c-file-style: bsd -*-

///
//   bwi_action_executor -- main program
//

#include "actions/Action.h"
#include "actions/ActionFactory.h"
#include "actions/LogicalNavigation.h"

#include "kr_interface.h"
#include "plan_concurrently.h"

#include <ros/ros.h>
#include <ros/console.h>

#include <boost/bind.hpp>
#include <boost/concept_check.hpp>
#include <boost/graph/graph_concepts.hpp>

#include <list>

using namespace bwi_actexec;
using namespace std;

const unsigned int MAX_N = 20;          // maximum number of plan steps

std::list<Action *> computePlan(const std::string& ,unsigned int);
bool checkPlan(const std::list<Action *> & plan, const std::string& goalSpecification);
std::list<Action *> repairOrReplan(const std::list<Action *> & plan, const std::string& goalSpecification, unsigned int max_changes);

int main(int argc, char** argv) {

	ros::init(argc, argv, "bwi_action_executor");
	ros::NodeHandle n;

	if( ros::console::set_logger_level(ROSCONSOLE_DEFAULT_NAME, ros::console::levels::Debug) ) {
		ros::console::notifyLoggerLevelsChanged();
	}

	ros::Rate loop(10);

	//noop updates the fluents with the current position
	LogicalNavigation setInitialState("noop");
	setInitialState.run();

	string goal = ":- not at(cor,n).";

	std::list<Action *> plan = computePlan(goal, MAX_N);

    std::list< Action *>::iterator planit1 = plan.begin();
    std::string ss1;
    int mycounter = 0;
    for (; planit1 != plan.end(); ++planit1){
        ss1 = ss1 + (*planit1)->toASP(0) + " ";
    }

    for (; planit1 != plan.end(); ++planit1){
        if ( (*planit1) == NULL ) {
            mycounter++;
        }
    }

    ROS_DEBUG_STREAM( "Plan Length is: " << plan.size());
    ROS_DEBUG_STREAM("The list of all the actions is: " << ss1);

	if(plan.empty())
		throw runtime_error("The plan to achieve " + goal + " is empty!");

	Action * currentAction = plan.front();
	plan.pop_front();

	unsigned int executed = 0;

	while (ros::ok()) {

		ros::spinOnce();
		if (!currentAction->hasFinished()) {
			ROS_DEBUG_STREAM("Executing the current action: " << currentAction->toASP(0));
			currentAction->run();
		}

		else {
                        // current action finished

			delete currentAction;

			executed++;

			ROS_DEBUG("Forward Projecting Plan to Check Validity...");
			bool valid = checkPlan(plan,goal);
			if(!valid) {
				ROS_DEBUG("Forward projection failed, trying repair");

				//plan repair

				int max_changes = min((MAX_N-executed-plan.size()), plan.size());
				std::list<Action *> newPlan = repairOrReplan(plan, goal, max_changes);

				//delete the old plan

				list<Action *>::iterator actIt = plan.begin();
				for(; actIt != plan.end(); ++actIt)
					delete *actIt;
				plan.clear();

				if (newPlan.empty()) {
					throw runtime_error("The plan to achieve " + goal + " is empty!");
				}
				else {
					ROS_DEBUG("replanning success");
					plan = newPlan;
				}

                // Print new plan
                std::list< Action *>::iterator planit2 = plan.begin();
                std::string ss2;
                for (; planit2 != plan.end(); ++planit2){
                    ss2 = ss2 + (*planit2)->toASP(0) + " ";
                }
                ROS_DEBUG_STREAM("Plan Length is: " << plan.size());
                ROS_DEBUG_STREAM("The list of all the actions is: " << ss2);

			}

			if (plan.empty())
				break;  // plan completed

			currentAction = plan.front();
			plan.pop_front();

		}
		loop.sleep();
	}

	return 0;
}

/// Compute a new plan to get from current state to goal.
//
//  @param goalSpecification string representing the goal state.
//  @param max_n maximum number of steps allowed in the plan.
//  @return list of pointer to actions for executing the plan.
//
//  @note: This runs in parallel with the main thread.  It MUST not
//         modify any variables used there.
//
std::list<Action *> computePlan(const std::string& goalSpecification, unsigned int max_n) {

	bwi_kr::AnswerSetMsg answerSet;

	for (int i=0; i<max_n && !answerSet.satisfied ; ++i) {

		stringstream goal;
		goal << goalSpecification << endl;
		goal << "#hide." << endl;

		ActionFactory::ActionMap::const_iterator actIt = ActionFactory::actions().begin();
		for( ; actIt != ActionFactory::actions().end(); ++actIt) {
			//the last parameter is always the the step number
			goal << "#show " << actIt->second->getName() << "/" << actIt->second->paramNumber() + 1 << "." << endl;
		}

		answerSet = kr_query(goal.str(),i,"planQuery.asp");

	}

	vector<bwi_kr::Predicate> &preds = answerSet.predicates;

	vector<Action *> planVector(preds.size());
	for (int j=0 ; j<preds.size() ; ++j) {

		Action *act = ActionFactory::byName(preds[j].name);
		act->init(preds[j].parameters);
		planVector[preds[j].timeStep] = act;
	}

	list<Action *> plan(planVector.begin(),planVector.end());

	return plan;
}

bool checkPlan(const std::list<Action *> & plan, const std::string& goalSpecification) {

	stringstream queryStream;

	list<Action *>::const_iterator planIt = plan.begin();

	for(unsigned int timeStep = 0; planIt != plan.end(); ++planIt, ++timeStep) {
		queryStream << (*planIt)->toASP(timeStep) << "." << endl;
	}

	queryStream << goalSpecification << endl;

	bwi_kr::AnswerSetMsg answerSet = kr_query(queryStream.str(),plan.size(), "checkPlan.asp");

	return answerSet.satisfied;

}

/// Try to repair the current plan.
//
//  @param plan list of action pointers representing the current plan.
//  @param goalSpecification string representing the goal state.
//  @param max_changes maximum number of plan changes to attempt.
//  @return list of pointers to actions for executing the amended plan.
//
//  @note: This runs in parallel with the main thread.  It MUST not
//         modify any variables used there.  Specifically, *plan* must
//         be a copy of the previously executing plan, so it can be
//         modified without interference.
//
std::list<Action *> repairPlan(const std::list<Action *> & plan,
                               const std::string& goalSpecification,
                               unsigned int max_changes) {

	ROS_DEBUG_STREAM( "repairing..." << "maximum number of changes is " << max_changes);

	bwi_kr::AnswerSetMsg answerSet;

	for (int i=1; (i<=max_changes) ; ++i) {

		int insert_N = i; //number of actions inserted to old plan
		int delete_N = 0; //number of actions deleted from old plan

		std::list<Action *> reusedPlan(plan.begin(), plan.end());

		while (insert_N >= 0) {
			ROS_DEBUG_STREAM("insert " << insert_N << " remove " << delete_N);

			stringstream queryStream;

			list<Action *>::const_iterator planIt = reusedPlan.begin();

			for(unsigned int timeStep = insert_N; planIt != reusedPlan.end(); ++planIt, ++timeStep) {
				queryStream << (*planIt)->toASP(timeStep) << "." << endl;
			}

			queryStream << goalSpecification << endl;
			ROS_DEBUG_STREAM(queryStream.str());
			queryStream << "#hide." << endl;

			ActionFactory::ActionMap::const_iterator actIt = ActionFactory::actions().begin();
			for( ; actIt != ActionFactory::actions().end(); ++actIt) {
				//the last parameter is always the the step number ???
				queryStream << "#show " << actIt->second->getName() << "/" << actIt->second->paramNumber() + 1 << "." << endl;
			}

			bwi_kr::AnswerSetMsg answerSet = kr_query(queryStream.str(),reusedPlan.size()+insert_N, "repairPlan.asp");
			if (answerSet.satisfied) {
				ROS_DEBUG("satisfied");
				vector<bwi_kr::Predicate> &preds = answerSet.predicates;
				vector<Action *> planVector(preds.size());

				for (int j=0 ; j<preds.size() ; ++j) {

					ROS_DEBUG_STREAM("predicate timestep: " << preds[i].timeStep);

				Action *act = ActionFactory::byName(preds[j].name);
				act->init(preds[j].parameters);
				planVector[preds[j].timeStep] = act;
				}

				std::list<Action *> repairedPlan(planVector.begin(),planVector.end());
				return repairedPlan;
			}

			insert_N--;
			delete_N++;

			if(!reusedPlan.empty())
				reusedPlan.pop_front();
		}
	}

	std::list<Action *> repairedPlan;
	return repairedPlan;
}

/// Try to repair the current plan or make a new one.
///
///  @param plan previous list of actions, no longer valid
///  @param goal desired goal
///  @return new plan to use, empty if unsuccessful
std::list<Action *> repairOrReplan(const std::list<Action *> & plan,
                                   const std::string& goal,
                                   unsigned int max_changes)
{
	ROS_DEBUG("replanning concurrently...");

        // Make a copy of the plan, for repairPlan to edit in another
        // thread.
        std::list<Action *> editablePlan(plan.begin(), plan.end());

        return plan_concurrently<Action *>(
                boost::bind(repairPlan, editablePlan, goal, max_changes),
                boost::bind(computePlan, goal, MAX_N));
}
<commit_msg>bwi_action_executor: pass plan to planRepair() by value (#6)<commit_after>
// -*- mode: C++ -*-
// -*- c-file-style: bsd -*-

///
//   bwi_action_executor -- main program
//

#include "actions/Action.h"
#include "actions/ActionFactory.h"
#include "actions/LogicalNavigation.h"

#include "kr_interface.h"
#include "plan_concurrently.h"

#include <ros/ros.h>
#include <ros/console.h>

#include <boost/bind.hpp>
#include <boost/concept_check.hpp>
#include <boost/graph/graph_concepts.hpp>

#include <list>

using namespace bwi_actexec;
using namespace std;

const unsigned int MAX_N = 20;          // maximum number of plan steps

std::list<Action *> computePlan(const std::string& ,unsigned int);
bool checkPlan(const std::list<Action *> & plan, const std::string& goalSpecification);
std::list<Action *> repairOrReplan(const std::list<Action *> & plan, const std::string& goalSpecification, unsigned int max_changes);

int main(int argc, char** argv) {

	ros::init(argc, argv, "bwi_action_executor");
	ros::NodeHandle n;

	if( ros::console::set_logger_level(ROSCONSOLE_DEFAULT_NAME, ros::console::levels::Debug) ) {
		ros::console::notifyLoggerLevelsChanged();
	}

	ros::Rate loop(10);

	//noop updates the fluents with the current position
	LogicalNavigation setInitialState("noop");
	setInitialState.run();

	string goal = ":- not at(cor,n).";

	std::list<Action *> plan = computePlan(goal, MAX_N);

    std::list< Action *>::iterator planit1 = plan.begin();
    std::string ss1;
    int mycounter = 0;
    for (; planit1 != plan.end(); ++planit1){
        ss1 = ss1 + (*planit1)->toASP(0) + " ";
    }

    for (; planit1 != plan.end(); ++planit1){
        if ( (*planit1) == NULL ) {
            mycounter++;
        }
    }

    ROS_DEBUG_STREAM( "Plan Length is: " << plan.size());
    ROS_DEBUG_STREAM("The list of all the actions is: " << ss1);

	if(plan.empty())
		throw runtime_error("The plan to achieve " + goal + " is empty!");

	Action * currentAction = plan.front();
	plan.pop_front();

	unsigned int executed = 0;

	while (ros::ok()) {

		ros::spinOnce();
		if (!currentAction->hasFinished()) {
			ROS_DEBUG_STREAM("Executing the current action: " << currentAction->toASP(0));
			currentAction->run();
		}

		else {
                        // current action finished

			delete currentAction;

			executed++;

			ROS_DEBUG("Forward Projecting Plan to Check Validity...");
			bool valid = checkPlan(plan,goal);
			if(!valid) {
				ROS_DEBUG("Forward projection failed, trying repair");

				//plan repair

				int max_changes = min((MAX_N-executed-plan.size()), plan.size());
				std::list<Action *> newPlan = repairOrReplan(plan, goal, max_changes);

				//delete the old plan

				list<Action *>::iterator actIt = plan.begin();
				for(; actIt != plan.end(); ++actIt)
					delete *actIt;
				plan.clear();

				if (newPlan.empty()) {
					throw runtime_error("The plan to achieve " + goal + " is empty!");
				}
				else {
					ROS_DEBUG("replanning success");
					plan = newPlan;
				}

                // Print new plan
                std::list< Action *>::iterator planit2 = plan.begin();
                std::string ss2;
                for (; planit2 != plan.end(); ++planit2){
                    ss2 = ss2 + (*planit2)->toASP(0) + " ";
                }
                ROS_DEBUG_STREAM("Plan Length is: " << plan.size());
                ROS_DEBUG_STREAM("The list of all the actions is: " << ss2);

			}

			if (plan.empty())
				break;  // plan completed

			currentAction = plan.front();
			plan.pop_front();

		}
		loop.sleep();
	}

	return 0;
}

/// Compute a new plan to get from current state to goal.
//
//  @param goalSpecification string representing the goal state.
//  @param max_n maximum number of steps allowed in the plan.
//  @return list of pointer to actions for executing the plan.
//
//  @note: This runs in parallel with the main thread.  It MUST not
//         modify any variables used there.
//
std::list<Action *> computePlan(const std::string& goalSpecification, unsigned int max_n) {

	bwi_kr::AnswerSetMsg answerSet;

	for (int i=0; i<max_n && !answerSet.satisfied ; ++i) {

		stringstream goal;
		goal << goalSpecification << endl;
		goal << "#hide." << endl;

		ActionFactory::ActionMap::const_iterator actIt = ActionFactory::actions().begin();
		for( ; actIt != ActionFactory::actions().end(); ++actIt) {
			//the last parameter is always the the step number
			goal << "#show " << actIt->second->getName() << "/" << actIt->second->paramNumber() + 1 << "." << endl;
		}

		answerSet = kr_query(goal.str(),i,"planQuery.asp");

	}

	vector<bwi_kr::Predicate> &preds = answerSet.predicates;

	vector<Action *> planVector(preds.size());
	for (int j=0 ; j<preds.size() ; ++j) {

		Action *act = ActionFactory::byName(preds[j].name);
		act->init(preds[j].parameters);
		planVector[preds[j].timeStep] = act;
	}

	list<Action *> plan(planVector.begin(),planVector.end());

	return plan;
}

bool checkPlan(const std::list<Action *> & plan, const std::string& goalSpecification) {

	stringstream queryStream;

	list<Action *>::const_iterator planIt = plan.begin();

	for(unsigned int timeStep = 0; planIt != plan.end(); ++planIt, ++timeStep) {
		queryStream << (*planIt)->toASP(timeStep) << "." << endl;
	}

	queryStream << goalSpecification << endl;

	bwi_kr::AnswerSetMsg answerSet = kr_query(queryStream.str(),plan.size(), "checkPlan.asp");

	return answerSet.satisfied;

}

/// Try to repair the current plan.
//
//  @param plan list of action pointers representing the current plan
//              (passed by value).
//  @param goalSpecification string representing the goal state.
//  @param max_changes maximum number of plan changes to attempt.
//  @return list of pointers to actions for executing the amended plan.
//
//  @note: This runs in parallel with the main thread.  It MUST not
//         modify any variables used there.  Specifically, *plan* must
//         be a copy of the previously executing plan, so it can be
//         modified without interference.
//
std::list<Action *> repairPlan(std::list<Action *> plan,
                               const std::string& goalSpecification,
                               unsigned int max_changes) {

	ROS_DEBUG_STREAM( "repairing..." << "maximum number of changes is " << max_changes);

	bwi_kr::AnswerSetMsg answerSet;

	for (int i=1; (i<=max_changes) ; ++i) {

		int insert_N = i; //number of actions inserted to old plan
		int delete_N = 0; //number of actions deleted from old plan

		std::list<Action *> reusedPlan(plan.begin(), plan.end());

		while (insert_N >= 0) {
			ROS_DEBUG_STREAM("insert " << insert_N << " remove " << delete_N);

			stringstream queryStream;

			list<Action *>::const_iterator planIt = reusedPlan.begin();

			for(unsigned int timeStep = insert_N; planIt != reusedPlan.end(); ++planIt, ++timeStep) {
				queryStream << (*planIt)->toASP(timeStep) << "." << endl;
			}

			queryStream << goalSpecification << endl;
			ROS_DEBUG_STREAM(queryStream.str());
			queryStream << "#hide." << endl;

			ActionFactory::ActionMap::const_iterator actIt = ActionFactory::actions().begin();
			for( ; actIt != ActionFactory::actions().end(); ++actIt) {
				//the last parameter is always the the step number ???
				queryStream << "#show " << actIt->second->getName() << "/" << actIt->second->paramNumber() + 1 << "." << endl;
			}

			bwi_kr::AnswerSetMsg answerSet = kr_query(queryStream.str(),reusedPlan.size()+insert_N, "repairPlan.asp");
			if (answerSet.satisfied) {
				ROS_DEBUG("satisfied");
				vector<bwi_kr::Predicate> &preds = answerSet.predicates;
				vector<Action *> planVector(preds.size());

				for (int j=0 ; j<preds.size() ; ++j) {

					ROS_DEBUG_STREAM("predicate timestep: " << preds[i].timeStep);

				Action *act = ActionFactory::byName(preds[j].name);
				act->init(preds[j].parameters);
				planVector[preds[j].timeStep] = act;
				}

				std::list<Action *> repairedPlan(planVector.begin(),planVector.end());
				return repairedPlan;
			}

			insert_N--;
			delete_N++;

			if(!reusedPlan.empty())
				reusedPlan.pop_front();
		}
	}

	std::list<Action *> repairedPlan;
	return repairedPlan;
}

/// Try to repair the current plan or make a new one.
///
///  @param plan previous list of actions, no longer valid
///  @param goal desired goal
///  @return new plan to use, empty if unsuccessful
std::list<Action *> repairOrReplan(const std::list<Action *> & plan,
                                   const std::string& goal,
                                   unsigned int max_changes)
{
	ROS_DEBUG("replanning concurrently...");

        // Make a copy of the plan, for repairPlan to edit in another
        // thread.
        std::list<Action *> editablePlan(plan.begin(), plan.end());

        return plan_concurrently<Action *>(
                boost::bind(repairPlan, editablePlan, goal, max_changes),
                boost::bind(computePlan, goal, MAX_N));
}
<|endoftext|>


<commit_before>#include <cstring>
#include <string>

#include "ccspec/core.h"
#include "ccspec/expectation.h"
#include "ccspec/matchers.h"

namespace spec {
namespace support {

using std::string;
using ccspec::core::describe;
using ccspec::core::it;
using ccspec::expect;
using ccspec::matchers::contain;
using ccspec::matchers::eq;
using ccspec::support::inspect;

auto inspect_spec = describe(".inspect", [] {
  it("returns the string value for char", [] {
    char c = 'x';
    expect(inspect(c)).to(eq("x"));
  });

  it("returns the string value for unsigned char", [] {
    unsigned char c = 'x';
    expect(inspect(c)).to(eq("x"));
  });

  it("returns the string value for bool", [] {
    bool b = true;
    expect(inspect(b)).to(eq("true"));
  });

  it("returns the value for short", [] {
    short s = 32767;
    expect(inspect(s)).to(eq("32767"));
  });

  it("returns the value for unsigned short", [] {
    unsigned short s = 65535;
    expect(inspect(s)).to(eq("65535"));
  });

  it("returns the value for int", [] {
    int i = 1234567;
    expect(inspect(i)).to(eq("1234567"));
  });

  it("returns the value for unsigned int", [] {
    unsigned int i = 1234567324;
    expect(inspect(i)).to(eq("1234567324"));
  });

  it("returns the value for long", [] {
    long l = 12345678;
    expect(inspect(l)).to(eq("12345678"));
  });

  it("returns the value for unsigned long", [] {
    unsigned long l = 12345673243;
    expect(inspect(l)).to(eq("12345673243"));
  });

  it("returns the value for long long", [] {
    long long l = 1234567890987654321;
    expect(inspect(l)).to(eq("1234567890987654321"));
  });

  it("returns the value for unsigned long long", [] {
    unsigned long long l = 5234567890987654321;
    expect(inspect(l)).to(eq("5234567890987654321"));
  });

  it("returns the value for unsigned long long", [] {
    unsigned long long l = 5234567890987654321;
    expect(inspect(l)).to(eq("5234567890987654321"));
  });

  it("returns the value for float", [] {
    float f = 3.14;
    expect(inspect(f)).to(eq("3.14"));
  });

  it("returns the value for double", [] {
    double f = 3.14159268373523;
    expect(inspect(f)).to(eq("3.14159268373523"));
  });

  it("returns the numerical value for void*", [] {
    void* p = reinterpret_cast<void*>(0xffffffff);
    expect(inspect(p)).to(eq("0xffffffff"));
  });

  it("returns the string value for const char*", [] {
    const char* s = "Hello, 世界!";
    expect(inspect(s)).to(eq("Hello, 世界!"));
  });

  it("returns the value for string", [] {
    string s = "你好, world!";
    expect(inspect(s)).to(eq("你好, world!"));
  });

  it("return the type name for arbitrary types", [] {
    class AbstractUserServiceProviderModule {
    } abstract_user_service_provider_module;
    expect(inspect(abstract_user_service_provider_module))
        .to(contain("AbstractUserServiceProviderModule3"));
  });
});

}  // namespace support
}  // namespace spec
<commit_msg>Fix test failure due to type in expectation<commit_after>#include <cstring>
#include <string>

#include "ccspec/core.h"
#include "ccspec/expectation.h"
#include "ccspec/matchers.h"

namespace spec {
namespace support {

using std::string;
using ccspec::core::describe;
using ccspec::core::it;
using ccspec::expect;
using ccspec::matchers::contain;
using ccspec::matchers::eq;
using ccspec::support::inspect;

auto inspect_spec = describe(".inspect", [] {
  it("returns the string value for char", [] {
    char c = 'x';
    expect(inspect(c)).to(eq("x"));
  });

  it("returns the string value for unsigned char", [] {
    unsigned char c = 'x';
    expect(inspect(c)).to(eq("x"));
  });

  it("returns the string value for bool", [] {
    bool b = true;
    expect(inspect(b)).to(eq("true"));
  });

  it("returns the value for short", [] {
    short s = 32767;
    expect(inspect(s)).to(eq("32767"));
  });

  it("returns the value for unsigned short", [] {
    unsigned short s = 65535;
    expect(inspect(s)).to(eq("65535"));
  });

  it("returns the value for int", [] {
    int i = 1234567;
    expect(inspect(i)).to(eq("1234567"));
  });

  it("returns the value for unsigned int", [] {
    unsigned int i = 1234567324;
    expect(inspect(i)).to(eq("1234567324"));
  });

  it("returns the value for long", [] {
    long l = 12345678;
    expect(inspect(l)).to(eq("12345678"));
  });

  it("returns the value for unsigned long", [] {
    unsigned long l = 12345673243;
    expect(inspect(l)).to(eq("12345673243"));
  });

  it("returns the value for long long", [] {
    long long l = 1234567890987654321;
    expect(inspect(l)).to(eq("1234567890987654321"));
  });

  it("returns the value for unsigned long long", [] {
    unsigned long long l = 5234567890987654321;
    expect(inspect(l)).to(eq("5234567890987654321"));
  });

  it("returns the value for unsigned long long", [] {
    unsigned long long l = 5234567890987654321;
    expect(inspect(l)).to(eq("5234567890987654321"));
  });

  it("returns the value for float", [] {
    float f = 3.14;
    expect(inspect(f)).to(eq("3.14"));
  });

  it("returns the value for double", [] {
    double f = 3.14159268373523;
    expect(inspect(f)).to(eq("3.14159268373523"));
  });

  it("returns the numerical value for void*", [] {
    void* p = reinterpret_cast<void*>(0xffffffff);
    expect(inspect(p)).to(eq("0xffffffff"));
  });

  it("returns the string value for const char*", [] {
    const char* s = "Hello, 世界!";
    expect(inspect(s)).to(eq("Hello, 世界!"));
  });

  it("returns the value for string", [] {
    string s = "你好, world!";
    expect(inspect(s)).to(eq("你好, world!"));
  });

  it("return the type name for arbitrary types", [] {
    class AbstractUserServiceProviderModule {
    } abstract_user_service_provider_module;
    expect(inspect(abstract_user_service_provider_module))
        .to(contain("AbstractUserServiceProviderModule"));
  });
});

}  // namespace support
}  // namespace spec
<|endoftext|>


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

#include <iostream>
#include <stdexcept>
#include <stdio.h>
#include <expat.h>
#include <string.h>
#include <fstream>
#include <string>
#include <stack>
#include <iomanip>

#include <votca/tools/property.h>
#include <votca/tools/colors.h>
#include <votca/tools/tokenizer.h>
#include <votca/tools/propertyiomanipulator.h>

#include <boost/format.hpp>
#include <boost/algorithm/string.hpp>
#include <unistd.h>

namespace votca { namespace tools {

// ostream modifier defines the output format, level, indentation
const int Property::IOindex = std::ios_base::xalloc();

Property &Property::get(const string &key)
{
    Tokenizer tok(key, ".");
    Tokenizer::iterator n;

    n = tok.begin();
    if(n==tok.end()) return *this;

    Property *p;
    map<string, Property*>::iterator iter;
    if(*n=="") {
        p = this;
    }
    else {
        iter = _map.find(*n);

        if(iter == _map.end())
            throw runtime_error("property not found: " + key);

        p = (((*iter).second));
    }
    ++n;
    try {
        for(; n!=tok.end(); ++n) {
            p = &p->get(*n);
        }
    }
    catch(string err) { // catch here to get full key in exception
        throw runtime_error("property not found: " + key);
    }

    return *p;
}

std::list<Property *> Property::Select(const string &filter)
{
    Tokenizer tok(filter, ".");

    std::list<Property *> selection;

    if(tok.begin()==tok.end()) return selection;

    selection.push_back(this);

    for (Tokenizer::iterator n = tok.begin();
            n != tok.end(); ++n) {
        std::list<Property *> childs;
        for (std::list<Property *>::iterator p = selection.begin();
                p != selection.end(); ++p) {
                for (list<Property>::iterator iter = (*p)->_properties.begin();
                    iter != (*p)->_properties.end(); ++iter) {
                    if (wildcmp((*n).c_str(), (*iter).name().c_str())) {
                        childs.push_back(&(*iter));
                    }
                }
        }
        selection = childs;
    }

    return selection;
}

static void start_hndl(void *data, const char *el, const char **attr)
{
    stack<Property *> *property_stack =
        (stack<Property *> *)XML_GetUserData((XML_Parser*)data);

    Property *cur = property_stack->top();
    Property &np = cur->add(el, "");

    for (int i = 0; attr[i]; i += 2)
        np.setAttribute(attr[i], attr[i + 1]);

    property_stack->push(&np);
}

static void end_hndl(void *data, const char *el)
{
    stack<Property *> *property_stack =
        (stack<Property *> *)XML_GetUserData((XML_Parser*)data);
    property_stack->pop();
}

void char_hndl(void *data, const char *txt, int txtlen)
{
    stack<Property *> *property_stack =
        (stack<Property *> *)XML_GetUserData((XML_Parser*)data);

    Property *cur = property_stack->top();
    cur->value().append(txt, txtlen);
}

bool load_property_from_xml(Property &p, string filename)
{
  XML_Parser parser = XML_ParserCreate(NULL);
  if (! parser)
    throw std::runtime_error("Couldn't allocate memory for xml parser");

  XML_UseParserAsHandlerArg(parser);
  XML_SetElementHandler(parser, start_hndl, end_hndl);
  XML_SetCharacterDataHandler(parser, char_hndl);

  ifstream fl;
  fl.open(filename.c_str());
  if(!fl.is_open())
    throw std::ios_base::failure("Error on open xml file: " + filename);

  stack<Property *> pstack;
  pstack.push(&p);

  XML_SetUserData(parser, (void*)&pstack);
  while(!fl.eof()) {
    string line;
    getline(fl, line);
    line=line + "\n";
    if (! XML_Parse(parser, line.c_str(), line.length(), fl.eof()))
      throw  std::ios_base::failure(filename + ": Parse error at line " +
          boost::lexical_cast<string>(XML_GetCurrentLineNumber(parser)) + "\n" +
          XML_ErrorString(XML_GetErrorCode(parser)));
  }
  fl.close();
  return true;
}

void PrintNodeTXT(std::ostream &out, Property &p, const int start_level, int level=0, string prefix="", string offset="")
{

    list<Property>::iterator iter;

    if((p.value() != "") || p.HasChilds() ) {

        if ( level >= start_level ) {
                if((p.value()).find_first_not_of("\t\n ") != std::string::npos)
                        out << offset << prefix << " = " << p.value() << endl;
        } else {
            prefix="";
        }
    }

    for(iter = p.begin(); iter!=p.end(); ++iter) {
        if(prefix=="") {
            level++;
            PrintNodeTXT(out, (*iter), start_level, level, prefix + (*iter).name(), offset );
            level--;
        } else {
            level++;
            PrintNodeTXT(out, (*iter), start_level, level, prefix + "." + (*iter).name(), offset );
            level--;
        }
    }

}

void PrintNodeXML(std::ostream &out, Property &p, PropertyIOManipulator *piom, int level=0, string offset="")
{
    list<Property>::iterator iter;
    Property::AttributeIterator ia;
    bool _endl = true;

    const ColorSchemeBase *color = &DEFAULT_COLORS;
    string indent("");
    int start_level(0);

    if ( piom ) {
        start_level = piom->getLevel();
        indent   = piom->getIndentation();
        color = piom->getColorScheme();
    }

    string cKey = color->Magenta();
    string cAttribute = color->Blue();
    string cAttributeValue = color->Green();
    string cReset = color->Reset();

    // print starting only from the start_level (the first node (level 0) can be <> </>)
        if ( level >= start_level )  {
            // print the node name
            out << indent << offset << "<" << cKey << p.name() << cReset;
            // print the node attributes
            for(ia = p.firstAttribute(); ia!=p.lastAttribute(); ++ia)
                out << indent << " "
                    << cAttribute << ia->first << cReset
                    << "=\""
                    << cReset << ia->second << cReset << "\"" ;
            out << ">";

            // print node value if it is not empty
            bool has_value = ( (p.value()).find_first_not_of("\t\n ") != std::string::npos );
            if( has_value ) {
                out <<  cAttributeValue <<  p.value() << cReset;
                _endl = false;
            }

            // check if we need the end of the line or not
            if( !has_value &&  p.HasChilds() ) out << endl;
            if( !has_value &&  !p.HasChilds() ) _endl = false;
        }

        // continue iteratively through the rest of the nodes
        for(iter = p.begin(); iter!=p.end(); ++iter) {
            level++;
            if (  level > start_level ) offset += "\t";
            PrintNodeXML(out, (*iter), piom, level, offset);
            if (  level > start_level ) offset.resize(offset.size()-1);
            level--;
        }

        if ( level >= start_level ) {
            if ( _endl ) {
                out << indent << offset << "</" << cKey << p.name() << cReset << ">" << endl;
            } else {
                out << indent << "</" << cKey << p.name() << cReset << ">" << endl;
            }
        }
}

void PrintNodeTEX(std::ostream &out, Property &p, PropertyIOManipulator *piom, int level=0, string prefix="") {

    list<Property>::iterator iter;
    string head_name;
    string _label(""); // reference of the xml file in the manual
    string _section(""); // reference of the description section in the manual
    string _help("");
    string _default(""); // default value if supplied
    string _unit(""); //unit, if supplied

   const ColorSchemeBase *color = &DEFAULT_COLORS;
   int start_level(0);

    if ( piom ) {
        start_level = piom->getLevel();
        color = piom->getColorScheme();
    }

    string header_format("\\subsection{%1%}\n"
                         "\\label{%2%}\n%3%\n"
                         "\\rowcolors{1}{invisiblegray}{white}\n"
                         "{\\small\n "
                         "\\begin{longtable}{m{3cm}|m{2cm}|m{1cm}|m{8cm}}\n"
                         " option & default & unit & description\\\\\n\\hline\n");

    string footer_format("\\end{longtable}\n}\n"
                         "\\noindent Return to the description of \\slink{%1%}{\\texttt{%2%}}.\n");

    string body_format(" \\hspace{%1%pt}\\hypertarget{%2%}{%3%} & %4% & %5% & %6% \\\\\n");

    // if this is the head node, print the header
    if ( level == start_level )  {
            head_name = p.name();
            _label = "calc:" + head_name;
            if ( p.hasAttribute("section") ) _section = p.getAttribute<string>("section");
            if ( p.hasAttribute("help") ) _help = p.getAttribute<string>("help");
            out << boost::format(header_format) % head_name % _label % _help;
            prefix = p.name();
     }

    if ( level > start_level ) {

        // if this node has children or a value or is not the first, start recursive printing
        if( ( p.value() != "" || p.HasChilds() ) && level > -1) {
            string _tex_name = boost::replace_all_copy( p.name(), "_", "\\_" );

            if ( p.hasAttribute("default") ) _default = p.getAttribute<string>("default");
            if ( p.hasAttribute("unit") ) _unit = p.getAttribute<string>("unit");
            if ( p.hasAttribute("help") ) _help = p.getAttribute<string>("help");

            out << boost::format(body_format) % int((level-start_level-1)*10)
                    % prefix
                    % _tex_name
                    % _default
                    % _unit
                    % _help;
        }
    }

    // continue iteratively through the rest of the nodes
    for(iter = p.begin(); iter != p.end(); ++iter) {
        if(prefix=="") {
            level++;
            PrintNodeTEX(out, (*iter), piom, level, prefix);
            level--;
        } else {
            level++;
            PrintNodeTEX(out, (*iter), piom, level, prefix);
            level--;
        }
    }

    // if this is the head node, print the footer
    if ( level == start_level )  out << boost::format(footer_format) % _section % head_name;
}

void PrintNodeHLP(std::ostream &out, Property &p, const int start_level=0, int level=0, string prefix="",  string offset="") {

    list<Property>::iterator iter;
    string head_name;
    string _help("");
    string _unit("");
    string _default("");
    string _name("");

    typedef Color<csRGB> ColorRGB; // use the RGB palette
    ColorRGB RGB; // Instance of an RGB palette
    string fmt =  "t|%1%%|15t|" + string(RGB.Blue()) + "%2%"
                                + string(RGB.Green()) + "%|40t|%3%%|55t|"
                                + string(RGB.Reset()) + "%4%\n";

    int _offset = level;

    // if this is the head node, print the header
    if ( level == start_level ) {
            head_name = string(RGB.Magenta()) + p.name();
            if ( p.hasAttribute("help") ) {
                if  ( p.hasAttribute("help") ) _help = string(RGB.Red()) + p.getAttribute<string>("help");
                out << boost::format(" %1%: %|18t| %2%" + string(RGB.Reset()) + "\n") % head_name % _help;
            }
            _offset=0;
            out << boost::format("%|3" + fmt) % "OPTION" % "DEFAULT" % "UNIT" % "DESCRIPTION";
    }

    if ( level > start_level ) {

            string ofmt;
            ofmt = "%|" + boost::lexical_cast<string>(_offset) + fmt;
            //cout << ofmt << " " << fmt << endl;
             if  ( p.hasAttribute("unit") )
                _unit = p.getAttribute<string>("unit");
            if  ( p.hasAttribute("default") )
                _default = p.getAttribute<string>("default");
            if  ( p.hasAttribute("help") ) _help = p.getAttribute<string>("help") ;
            if ( !_unit.empty() ) _unit = "[" + _unit + "]";
            if ( !_default.empty() ) _default = "(" + _default + ")";

             _name = p.name();

            out << boost::format(ofmt)
                    % _name
                    % _default
                    % _unit
                    % _help;
    }

    for(iter = p.begin(); iter != p.end(); ++iter) {
        if(prefix=="") {
            _offset = level + 2; level++;
            PrintNodeHLP(out, (*iter), start_level, level, (*iter).name(), offset);
            _offset = level - 2; level--;
        } else {
            _offset = level + 2; level++;
            PrintNodeHLP(out, (*iter), start_level, level, prefix + "." + (*iter).name(), offset);
            _offset = level - 2; level--;
        }
    }
}

std::ostream &operator<<(std::ostream &out, Property& p)
{
    if (!out.good())
        return out;

    std::ostream::sentry sentry(out);

    if(sentry)
    {
        // get the property format object attached to the stream
        PropertyIOManipulator *pm =
                (PropertyIOManipulator*)out.pword(Property::getIOindex());

        string _indentation("");
        int _level = 0;

        PropertyIOManipulator::Type _type = PropertyIOManipulator::XML;
        if (pm) {
            _indentation = pm->getIndentation();
            _level = pm->getLevel();
            _type = pm->getType();
            // check if we > or >> to a file and remove color codes
            if ( out.tellp() != -1 ) {
                pm->generateColorScheme<csDefault>();
            }

        }

         switch( _type )
        {
        default:
            PrintNodeTXT(out, p, _level);
            case PropertyIOManipulator::XML:
            PrintNodeXML(out, p, pm);
            break;
        case PropertyIOManipulator::TXT:
            PrintNodeTXT(out, p, _level, 0, "", _indentation);
            break;
         case PropertyIOManipulator::TEX:
            PrintNodeTEX(out, p, pm);
            break;
        case PropertyIOManipulator::HLP:
            PrintNodeHLP(out, p, _level, 0, "", _indentation);
            break;
        }
        //out << endl;
    }

    return out;
};

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

#include <iostream>
#include <stdexcept>
#include <stdio.h>
#include <expat.h>
#include <string.h>
#include <fstream>
#include <string>
#include <stack>
#include <iomanip>

#include <votca/tools/property.h>
#include <votca/tools/colors.h>
#include <votca/tools/tokenizer.h>
#include <votca/tools/propertyiomanipulator.h>

#include <boost/format.hpp>
#include <boost/algorithm/string.hpp>
#include <unistd.h>

namespace votca { namespace tools {

// ostream modifier defines the output format, level, indentation
const int Property::IOindex = std::ios_base::xalloc();

Property &Property::get(const string &key)
{
    Tokenizer tok(key, ".");
    Tokenizer::iterator n;

    n = tok.begin();
    if(n==tok.end()) return *this;

    Property *p;
    map<string, Property*>::iterator iter;
    if(*n=="") {
        p = this;
    }
    else {
        iter = _map.find(*n);

        if(iter == _map.end())
            throw runtime_error("property not found: " + key);

        p = (((*iter).second));
    }
    ++n;
    try {
        for(; n!=tok.end(); ++n) {
            p = &p->get(*n);
        }
    }
    catch(string err) { // catch here to get full key in exception
        throw runtime_error("property not found: " + key);
    }

    return *p;
}

std::list<Property *> Property::Select(const string &filter)
{
    Tokenizer tok(filter, ".");

    std::list<Property *> selection;

    if(tok.begin()==tok.end()) return selection;

    selection.push_back(this);

    for (Tokenizer::iterator n = tok.begin();
            n != tok.end(); ++n) {
        std::list<Property *> childs;
        for (std::list<Property *>::iterator p = selection.begin();
                p != selection.end(); ++p) {
                for (list<Property>::iterator iter = (*p)->_properties.begin();
                    iter != (*p)->_properties.end(); ++iter) {
                    if (wildcmp((*n).c_str(), (*iter).name().c_str())) {
                        childs.push_back(&(*iter));
                    }
                }
        }
        selection = childs;
    }

    return selection;
}

static void start_hndl(void *data, const char *el, const char **attr)
{
    stack<Property *> *property_stack =
        (stack<Property *> *)XML_GetUserData((XML_Parser*)data);

    Property *cur = property_stack->top();
    Property &np = cur->add(el, "");

    for (int i = 0; attr[i]; i += 2)
        np.setAttribute(attr[i], attr[i + 1]);

    property_stack->push(&np);
}

static void end_hndl(void *data, const char *el)
{
    stack<Property *> *property_stack =
        (stack<Property *> *)XML_GetUserData((XML_Parser*)data);
    property_stack->pop();
}

void char_hndl(void *data, const char *txt, int txtlen)
{
    stack<Property *> *property_stack =
        (stack<Property *> *)XML_GetUserData((XML_Parser*)data);

    Property *cur = property_stack->top();
    cur->value().append(txt, txtlen);
}

bool load_property_from_xml(Property &p, string filename)
{
  XML_Parser parser = XML_ParserCreate(NULL);
  if (! parser)
    throw std::runtime_error("Couldn't allocate memory for xml parser");

  XML_UseParserAsHandlerArg(parser);
  XML_SetElementHandler(parser, start_hndl, end_hndl);
  XML_SetCharacterDataHandler(parser, char_hndl);

  ifstream fl;
  fl.open(filename.c_str());
  if(!fl.is_open())
    throw std::ios_base::failure("Error on open xml file: " + filename);

  stack<Property *> pstack;
  pstack.push(&p);

  XML_SetUserData(parser, (void*)&pstack);
  while(!fl.eof()) {
    string line;
    getline(fl, line);
    line=line + "\n";
    if (! XML_Parse(parser, line.c_str(), line.length(), fl.eof()))
      throw  std::ios_base::failure(filename + ": Parse error at line " +
          boost::lexical_cast<string>(XML_GetCurrentLineNumber(parser)) + "\n" +
          XML_ErrorString(XML_GetErrorCode(parser)));
  }
  fl.close();
  return true;
}

void PrintNodeTXT(std::ostream &out, Property &p, const int start_level, int level=0, string prefix="", string offset="")
{

    list<Property>::iterator iter;

    if((p.value() != "") || p.HasChilds() ) {

        if ( level >= start_level ) {
                if((p.value()).find_first_not_of("\t\n ") != std::string::npos)
                        out << offset << prefix << " = " << p.value() << endl;
        } else {
            prefix="";
        }
    }

    for(iter = p.begin(); iter!=p.end(); ++iter) {
        if(prefix=="") {
            level++;
            PrintNodeTXT(out, (*iter), start_level, level, prefix + (*iter).name(), offset );
            level--;
        } else {
            level++;
            PrintNodeTXT(out, (*iter), start_level, level, prefix + "." + (*iter).name(), offset );
            level--;
        }
    }

}

void PrintNodeXML(std::ostream &out, Property &p, PropertyIOManipulator *piom, int level=0, string offset="")
{
    list<Property>::iterator iter;
    Property::AttributeIterator ia;
    bool _endl = true;

    const ColorSchemeBase *color = &DEFAULT_COLORS;
    string indent("");
    int start_level(0);

    if ( piom ) {
        start_level = piom->getLevel();
        indent   = piom->getIndentation();
        color = piom->getColorScheme();
    }

    string cKey = color->Magenta();
    string cAttribute = color->Blue();
    string cAttributeValue = color->Green();
    string cReset = color->Reset();

    // print starting only from the start_level (the first node (level 0) can be <> </>)
        if ( level >= start_level )  {
            // print the node name
            out << indent << offset << "<" << cKey << p.name() << cReset;
            // print the node attributes
            for(ia = p.firstAttribute(); ia!=p.lastAttribute(); ++ia)
                out << indent << " "
                    << cAttribute << ia->first << cReset
                    << "=\""
                    << cReset << ia->second << cReset << "\"" ;
            out << ">";

            // print node value if it is not empty
            bool has_value = ( (p.value()).find_first_not_of("\t\n ") != std::string::npos );
            if( has_value ) {
                out <<  cAttributeValue <<  p.value() << cReset;
                _endl = false;
            }

            // check if we need the end of the line or not
            if( !has_value &&  p.HasChilds() ) out << endl;
            if( !has_value &&  !p.HasChilds() ) _endl = false;
        }

        // continue iteratively through the rest of the nodes
        for(iter = p.begin(); iter!=p.end(); ++iter) {
            level++;
            if (  level > start_level ) offset += "\t";
            PrintNodeXML(out, (*iter), piom, level, offset);
            if (  level > start_level ) offset.resize(offset.size()-1);
            level--;
        }

        if ( level >= start_level ) {
            if ( _endl ) {
                out << indent << offset << "</" << cKey << p.name() << cReset << ">" << endl;
            } else {
                out << indent << "</" << cKey << p.name() << cReset << ">" << endl;
            }
        }
}

void PrintNodeTEX(std::ostream &out, Property &p, PropertyIOManipulator *piom, int level=0, string prefix="") {

    list<Property>::iterator iter;
    string head_name;
    string _label(""); // reference of the xml file in the manual
    string _section(""); // reference of the description section in the manual
    string _help("");
    string _default(""); // default value if supplied
    string _unit(""); //unit, if supplied

   const ColorSchemeBase *color = &DEFAULT_COLORS;
   int start_level(0);

    if ( piom ) {
        start_level = piom->getLevel();
        color = piom->getColorScheme();
    }

    string header_format("\\subsection{%1%}\n"
                         "\\label{%2%}\n%3%\n"
                         "\\rowcolors{1}{invisiblegray}{white}\n"
                         "{\\small\n "
                         "\\begin{longtable}{m{3cm}|m{2cm}|m{1cm}|m{8cm}}\n"
                         " option & default & unit & description\\\\\n\\hline\n");

    string footer_format("\\end{longtable}\n}\n"
                         "\\noindent Return to the description of \\slink{%1%}{\\texttt{%2%}}.\n");

    string body_format(" \\hspace{%1%pt}\\hypertarget{%2%}{%3%} & %4% & %5% & %6% \\\\\n");

    // if this is the head node, print the header
    if ( level == start_level )  {
            head_name = p.name();
            _label = "calc:" + head_name;
            if ( p.hasAttribute("section") ) _section = p.getAttribute<string>("section");
            if ( p.hasAttribute("help") ) _help = p.getAttribute<string>("help");
            out << boost::format(header_format) % head_name % _label % _help;
            prefix = p.name();
     }

    if ( level > start_level ) {

        // if this node has children or a value or is not the first, start recursive printing
        if( ( p.value() != "" || p.HasChilds() ) && level > -1) {
            string _tex_name = boost::replace_all_copy( p.name(), "_", "\\_" );

            if ( p.hasAttribute("default") ) _default = p.getAttribute<string>("default");
            if ( p.hasAttribute("unit") ) _unit = p.getAttribute<string>("unit");
            if ( p.hasAttribute("help") ) _help = p.getAttribute<string>("help");

            out << boost::format(body_format) % int((level-start_level-1)*10)
                    % prefix
                    % _tex_name
                    % _default
                    % _unit
                    % _help;
        }
    }

    // continue iteratively through the rest of the nodes
    for(iter = p.begin(); iter != p.end(); ++iter) {
        if(prefix=="") {
            level++;
            PrintNodeTEX(out, (*iter), piom, level, prefix);
            level--;
        } else {
            level++;
            PrintNodeTEX(out, (*iter), piom, level, prefix);
            level--;
        }
    }

    // if this is the head node, print the footer
    if ( level == start_level )  out << boost::format(footer_format) % _section % head_name;
}

void PrintNodeHLP(std::ostream &out, Property &p, const int start_level=0, int level=0, string prefix="",  string offset="") {

    list<Property>::iterator iter;
    string head_name;
    string _help("");
    string _unit("");
    string _default("");
    string _name("");

    typedef Color<csRGB> ColorRGB; // use the RGB palette
    ColorRGB RGB; // Instance of an RGB palette
    string fmt =  "t|%1%%|15t|" + string(RGB.Blue()) + "%2%"
                                + string(RGB.Green()) + "%|40t|%3%%|55t|"
                                + string(RGB.Reset()) + "%4%\n";

    int _offset = level;

    // if this is the head node, print the header
    if ( level == start_level ) {
            head_name = string(RGB.Magenta()) + p.name();
            if ( p.hasAttribute("help") ) {
                if  ( p.hasAttribute("help") ) _help = string(RGB.Red()) + p.getAttribute<string>("help");
                out << boost::format(" %1%: %|18t| %2%" + string(RGB.Reset()) + "\n") % head_name % _help;
            }
            _offset=0;
            out << boost::format("%|3" + fmt) % "OPTION" % "DEFAULT" % "UNIT" % "DESCRIPTION";
    }

    if ( level > start_level ) {

            string ofmt;
            ofmt = "%|" + boost::lexical_cast<string>(_offset) + fmt;
            //cout << ofmt << " " << fmt << endl;
             if  ( p.hasAttribute("unit") )
                _unit = p.getAttribute<string>("unit");
            if  ( p.hasAttribute("default") )
                _default = p.getAttribute<string>("default");
            if  ( p.hasAttribute("help") ) _help = p.getAttribute<string>("help") ;
            if ( !_unit.empty() ) _unit = "[" + _unit + "]";
            if ( !_default.empty() ) _default = "(" + _default + ")";

             _name = p.name();

            out << boost::format(ofmt)
                    % _name
                    % _default
                    % _unit
                    % _help;
    }

    for(iter = p.begin(); iter != p.end(); ++iter) {
        if(prefix=="") {
            _offset = level + 2; level++;
            PrintNodeHLP(out, (*iter), start_level, level, (*iter).name(), offset);
            _offset = level - 2; level--;
        } else {
            _offset = level + 2; level++;
            PrintNodeHLP(out, (*iter), start_level, level, prefix + "." + (*iter).name(), offset);
            _offset = level - 2; level--;
        }
    }
}

std::ostream &operator<<(std::ostream &out, Property& p)
{
    if (!out.good())
        return out;

    std::ostream::sentry sentry(out);

    if(sentry)
    {
        // get the property format object attached to the stream
        PropertyIOManipulator *pm =
                (PropertyIOManipulator*)out.pword(Property::getIOindex());

        string _indentation("");
        int _level = 0;

        PropertyIOManipulator::Type _type = PropertyIOManipulator::XML;
        if (pm) {
            _indentation = pm->getIndentation();
            _level = pm->getLevel();
            _type = pm->getType();
            // check if we > or >> to a file and remove color codes
            if( !isatty(STDOUT_FILENO) || !isatty(STDERR_FILENO) ) {
                pm->generateColorScheme<csDefault>();
            }

        }

         switch( _type )
        {
        default:
            PrintNodeTXT(out, p, _level);
            case PropertyIOManipulator::XML:
            PrintNodeXML(out, p, pm);
            break;
        case PropertyIOManipulator::TXT:
            PrintNodeTXT(out, p, _level, 0, "", _indentation);
            break;
         case PropertyIOManipulator::TEX:
            PrintNodeTEX(out, p, pm);
            break;
        case PropertyIOManipulator::HLP:
            PrintNodeHLP(out, p, _level, 0, "", _indentation);
            break;
        }
        //out << endl;
    }

    return out;
};

}}
<|endoftext|>


<commit_before>// $Id$

/*
 Copyright (c) 2007-2011, Trustees of The Leland Stanford Junior University
 All rights reserved.

 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions are met:

 Redistributions of source code must retain the above copyright notice, this
 list of conditions and the following disclaimer.
 Redistributions in binary form must reproduce the above copyright notice, this
 list of conditions and the following disclaimer in the documentation and/or
 other materials provided with the distribution.

 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 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.
*/

#ifndef _BUFFER_STATE_HPP_
#define _BUFFER_STATE_HPP_

#include <vector>
#include <queue>

#include "module.hpp"
#include "flit.hpp"
#include "credit.hpp"
#include "config_utils.hpp"

class BufferState : public Module {

  class BufferPolicy : public Module {
  protected:
    BufferState const * const _buffer_state;
  public:
    BufferPolicy(Configuration const & config, BufferState * parent,
		 const string & name);
    virtual void SetMinLatency(int min_latency) {}
    virtual void TakeBuffer(int vc);
    virtual void SendingFlit(Flit const * const f);
    virtual void FreeSlotFor(int vc);
    virtual bool IsFullFor(int vc) const = 0;
    virtual int AvailableFor(int vc) const = 0;
    virtual int LimitFor(int vc) const = 0;

    static BufferPolicy * New(Configuration const & config,
			      BufferState * parent, const string & name);
  };

  class PrivateBufferPolicy : public BufferPolicy {
  protected:
    int _vc_buf_size;
  public:
    PrivateBufferPolicy(Configuration const & config, BufferState * parent,
			const string & name);
    virtual void SendingFlit(Flit const * const f);
    virtual bool IsFullFor(int vc = 0) const;
    virtual int AvailableFor(int vc = 0) const;
    virtual int LimitFor(int vc = 0) const;
  };

  class SharedBufferPolicy : public BufferPolicy {
  protected:
    int _buf_size;
    vector<int> _private_buf_vc_map;
    vector<int> _private_buf_size;
    vector<int> _private_buf_occupancy;
    int _shared_buf_size;
    int _shared_buf_occupancy;
    vector<int> _reserved_slots;
    void ProcessFreeSlot(int vc = 0);
  public:
    SharedBufferPolicy(Configuration const & config, BufferState * parent,
		       const string & name);
    virtual void SendingFlit(Flit const * const f);
    virtual void FreeSlotFor(int vc = 0);
    virtual bool IsFullFor(int vc = 0) const;
    virtual int AvailableFor(int vc = 0) const;
    virtual int LimitFor(int vc = 0) const;
  };

  class LimitedSharedBufferPolicy : public SharedBufferPolicy {
  protected:
    int _vcs;
    int _active_vcs;
    int _max_held_slots;
  public:
    LimitedSharedBufferPolicy(Configuration const & config,
			      BufferState * parent,
			      const string & name);
    virtual void TakeBuffer(int vc = 0);
    virtual void SendingFlit(Flit const * const f);
    virtual bool IsFullFor(int vc = 0) const;
    virtual int AvailableFor(int vc = 0) const;
    virtual int LimitFor(int vc = 0) const;
  };

  class DynamicLimitedSharedBufferPolicy : public LimitedSharedBufferPolicy {
  public:
    DynamicLimitedSharedBufferPolicy(Configuration const & config,
				     BufferState * parent,
				     const string & name);
    virtual void TakeBuffer(int vc = 0);
    virtual void SendingFlit(Flit const * const f);
  };

  class ShiftingDynamicLimitedSharedBufferPolicy : public DynamicLimitedSharedBufferPolicy {
  public:
    ShiftingDynamicLimitedSharedBufferPolicy(Configuration const & config,
					     BufferState * parent,
					     const string & name);
    virtual void TakeBuffer(int vc = 0);
    virtual void SendingFlit(Flit const * const f);
  };

  class FeedbackSharedBufferPolicy : public SharedBufferPolicy {
  protected:
    int _ComputeRTT(int vc, int last_rtt) const;
    int _ComputeLimit(int rtt) const;
    int _ComputeMaxSlots(int vc) const;
    int _vcs;
    vector<int> _occupancy_limit;
    vector<int> _round_trip_time;
    vector<queue<int> > _flit_sent_time;
    int _min_latency;
    int _total_mapped_size;
    int _aging_scale;
    int _offset;
  public:
    FeedbackSharedBufferPolicy(Configuration const & config,
			       BufferState * parent, const string & name);
    virtual void SetMinLatency(int min_latency);
    virtual void SendingFlit(Flit const * const f);
    virtual void FreeSlotFor(int vc = 0);
    virtual bool IsFullFor(int vc = 0) const;
    virtual int AvailableFor(int vc = 0) const;
    virtual int LimitFor(int vc = 0) const;
  };

  class SimpleFeedbackSharedBufferPolicy : public FeedbackSharedBufferPolicy {
  protected:
    vector<int> _pending_credits;
  public:
    SimpleFeedbackSharedBufferPolicy(Configuration const & config,
				     BufferState * parent, const string & name);
    virtual void SendingFlit(Flit const * const f);
    virtual void FreeSlotFor(int vc = 0);
  };

  bool _wait_for_tail_credit;
  int  _size;
  int  _occupancy;
  vector<int> _vc_occupancy;
  int  _vcs;

  BufferPolicy * _buffer_policy;

  vector<bool> _in_use;
  vector<bool> _tail_sent;
  vector<int> _last_id;
  vector<int> _last_pid;

#ifdef TRACK_BUFFERS
  int _classes;
  vector<queue<int> > _outstanding_classes;
  vector<int> _class_occupancy;
#endif

public:

  BufferState( const Configuration& config,
	       Module *parent, const string& name );

  ~BufferState();

  inline void SetMinLatency(int min_latency) {
    _buffer_policy->SetMinLatency(min_latency);
  }

  void ProcessCredit( Credit const * const c );
  void SendingFlit( Flit const * const f );

  void TakeBuffer( int vc = 0 );

  inline bool IsFull() const {
    return (_occupancy < _size);
  }
  inline bool IsFullFor( int vc = 0 ) const {
    return _buffer_policy->IsFullFor(vc);
  }
  inline int AvailableFor( int vc = 0 ) const {
    return _buffer_policy->AvailableFor(vc);
  }
  inline int LimitFor( int vc = 0 ) const {
    return _buffer_policy->LimitFor(vc);
  }
  inline bool IsEmptyFor(int vc = 0) const {
    assert((vc >= 0) && (vc < _vcs));
    return (_vc_occupancy[vc] == 0);
  }
  inline bool IsAvailableFor( int vc = 0 ) const {
    assert( ( vc >= 0 ) && ( vc < _vcs ) );
    return !_in_use[vc];
  }

  inline int Occupancy() const {
    return _occupancy;
  }

  inline int OccupancyFor( int vc = 0 ) const {
    assert((vc >= 0) && (vc < _vcs));
    return _vc_occupancy[vc];
  }

#ifdef TRACK_BUFFERS
  inline int OccupancyForClass(int c) const {
    assert((c >= 0) && (c < _classes));
    return _class_occupancy[c];
  }
#endif

  void Display( ostream & os = cout ) const;
};

#endif
<commit_msg>undo bad commit<commit_after>// $Id$

/*
 Copyright (c) 2007-2011, Trustees of The Leland Stanford Junior University
 All rights reserved.

 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions are met:

 Redistributions of source code must retain the above copyright notice, this
 list of conditions and the following disclaimer.
 Redistributions in binary form must reproduce the above copyright notice, this
 list of conditions and the following disclaimer in the documentation and/or
 other materials provided with the distribution.

 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 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.
*/

#ifndef _BUFFER_STATE_HPP_
#define _BUFFER_STATE_HPP_

#include <vector>
#include <queue>

#include "module.hpp"
#include "flit.hpp"
#include "credit.hpp"
#include "config_utils.hpp"

class BufferState : public Module {

  class BufferPolicy : public Module {
  protected:
    BufferState const * const _buffer_state;
  public:
    BufferPolicy(Configuration const & config, BufferState * parent,
		 const string & name);
    virtual void SetMinLatency(int min_latency) {}
    virtual void TakeBuffer(int vc = 0);
    virtual void SendingFlit(Flit const * const f);
    virtual void FreeSlotFor(int vc = 0);
    virtual bool IsFullFor(int vc = 0) const = 0;
    virtual int AvailableFor(int vc = 0) const = 0;
    virtual int LimitFor(int vc = 0) const = 0;

    static BufferPolicy * New(Configuration const & config,
			      BufferState * parent, const string & name);
  };

  class PrivateBufferPolicy : public BufferPolicy {
  protected:
    int _vc_buf_size;
  public:
    PrivateBufferPolicy(Configuration const & config, BufferState * parent,
			const string & name);
    virtual void SendingFlit(Flit const * const f);
    virtual bool IsFullFor(int vc = 0) const;
    virtual int AvailableFor(int vc = 0) const;
    virtual int LimitFor(int vc = 0) const;
  };

  class SharedBufferPolicy : public BufferPolicy {
  protected:
    int _buf_size;
    vector<int> _private_buf_vc_map;
    vector<int> _private_buf_size;
    vector<int> _private_buf_occupancy;
    int _shared_buf_size;
    int _shared_buf_occupancy;
    vector<int> _reserved_slots;
    void ProcessFreeSlot(int vc = 0);
  public:
    SharedBufferPolicy(Configuration const & config, BufferState * parent,
		       const string & name);
    virtual void SendingFlit(Flit const * const f);
    virtual void FreeSlotFor(int vc = 0);
    virtual bool IsFullFor(int vc = 0) const;
    virtual int AvailableFor(int vc = 0) const;
    virtual int LimitFor(int vc = 0) const;
  };

  class LimitedSharedBufferPolicy : public SharedBufferPolicy {
  protected:
    int _vcs;
    int _active_vcs;
    int _max_held_slots;
  public:
    LimitedSharedBufferPolicy(Configuration const & config,
			      BufferState * parent,
			      const string & name);
    virtual void TakeBuffer(int vc = 0);
    virtual void SendingFlit(Flit const * const f);
    virtual bool IsFullFor(int vc = 0) const;
    virtual int AvailableFor(int vc = 0) const;
    virtual int LimitFor(int vc = 0) const;
  };

  class DynamicLimitedSharedBufferPolicy : public LimitedSharedBufferPolicy {
  public:
    DynamicLimitedSharedBufferPolicy(Configuration const & config,
				     BufferState * parent,
				     const string & name);
    virtual void TakeBuffer(int vc = 0);
    virtual void SendingFlit(Flit const * const f);
  };

  class ShiftingDynamicLimitedSharedBufferPolicy : public DynamicLimitedSharedBufferPolicy {
  public:
    ShiftingDynamicLimitedSharedBufferPolicy(Configuration const & config,
					     BufferState * parent,
					     const string & name);
    virtual void TakeBuffer(int vc = 0);
    virtual void SendingFlit(Flit const * const f);
  };

  class FeedbackSharedBufferPolicy : public SharedBufferPolicy {
  protected:
    int _ComputeRTT(int vc, int last_rtt) const;
    int _ComputeLimit(int rtt) const;
    int _ComputeMaxSlots(int vc) const;
    int _vcs;
    vector<int> _occupancy_limit;
    vector<int> _round_trip_time;
    vector<queue<int> > _flit_sent_time;
    int _min_latency;
    int _total_mapped_size;
    int _aging_scale;
    int _offset;
  public:
    FeedbackSharedBufferPolicy(Configuration const & config,
			       BufferState * parent, const string & name);
    virtual void SetMinLatency(int min_latency);
    virtual void SendingFlit(Flit const * const f);
    virtual void FreeSlotFor(int vc = 0);
    virtual bool IsFullFor(int vc = 0) const;
    virtual int AvailableFor(int vc = 0) const;
    virtual int LimitFor(int vc = 0) const;
  };

  class SimpleFeedbackSharedBufferPolicy : public FeedbackSharedBufferPolicy {
  protected:
    vector<int> _pending_credits;
  public:
    SimpleFeedbackSharedBufferPolicy(Configuration const & config,
				     BufferState * parent, const string & name);
    virtual void SendingFlit(Flit const * const f);
    virtual void FreeSlotFor(int vc = 0);
  };

  bool _wait_for_tail_credit;
  int  _size;
  int  _occupancy;
  vector<int> _vc_occupancy;
  int  _vcs;

  BufferPolicy * _buffer_policy;

  vector<bool> _in_use;
  vector<bool> _tail_sent;
  vector<int> _last_id;
  vector<int> _last_pid;

#ifdef TRACK_BUFFERS
  int _classes;
  vector<queue<int> > _outstanding_classes;
  vector<int> _class_occupancy;
#endif

public:

  BufferState( const Configuration& config,
	       Module *parent, const string& name );

  ~BufferState();

  inline void SetMinLatency(int min_latency) {
    _buffer_policy->SetMinLatency(min_latency);
  }

  void ProcessCredit( Credit const * const c );
  void SendingFlit( Flit const * const f );

  void TakeBuffer( int vc = 0 );

  inline bool IsFull() const {
    return (_occupancy < _size);
  }
  inline bool IsFullFor( int vc = 0 ) const {
    return _buffer_policy->IsFullFor(vc);
  }
  inline int AvailableFor( int vc = 0 ) const {
    return _buffer_policy->AvailableFor(vc);
  }
  inline int LimitFor( int vc = 0 ) const {
    return _buffer_policy->LimitFor(vc);
  }
  inline bool IsEmptyFor(int vc = 0) const {
    assert((vc >= 0) && (vc < _vcs));
    return (_vc_occupancy[vc] == 0);
  }
  inline bool IsAvailableFor( int vc = 0 ) const {
    assert( ( vc >= 0 ) && ( vc < _vcs ) );
    return !_in_use[vc];
  }

  inline int Occupancy() const {
    return _occupancy;
  }

  inline int OccupancyFor( int vc = 0 ) const {
    assert((vc >= 0) && (vc < _vcs));
    return _vc_occupancy[vc];
  }

#ifdef TRACK_BUFFERS
  inline int OccupancyForClass(int c) const {
    assert((c >= 0) && (c < _classes));
    return _class_occupancy[c];
  }
#endif

  void Display( ostream & os = cout ) const;
};

#endif
<|endoftext|>


<commit_before>// SimpleGraph.cpp : implementation file
//

#include "stdafx.h"
#include "SimpleGraph.h"
#include "misc.h"

// CSimpleGraph

IMPLEMENT_DYNAMIC(CSimpleGraph, CWnd)

CSimpleGraph::CSimpleGraph()
{
	m_pdc = NULL;
	m_bar = -1;
}

void CSimpleGraph::SetData(FILE_ADDRESS maximum, std::vector<FILE_ADDRESS> val, std::vector<COLORREF> col)
{
	if (val == m_val && col == m_col)
		return;                         // no change

	ASSERT(val.size() == col.size());
	m_max = maximum;
	m_val.swap(val);
	m_col.swap(col);

	update();
}

void CSimpleGraph::update()
{
	CRect rct;
	GetClientRect(rct);

	// If the graph has been resized we need a new DC/bitmap
	if (m_rct != rct)
	{
		m_rct = rct;

		// Delete old DC/bitmap
		if (m_pdc != NULL)
		{
			delete m_pdc;
			m_pdc = NULL;
			m_bm.DeleteObject();
			m_hfnt.DeleteObject();
			m_vfnt.DeleteObject();
		}

		// Recreate DC/bitmap for drawing into
		CDC* pDC = GetDC();
		m_pdc = new CDC();
		m_pdc->CreateCompatibleDC(pDC);
		m_bm.CreateCompatibleBitmap(pDC, rct.Width(), rct.Height());
		m_pdc->SelectObject(&m_bm);

		// Set up text including creating the font
		LOGFONT lf;
		memset(&lf, '\0', sizeof(lf));
		lf.lfHeight = 12;
		strcpy(lf.lfFaceName, "Tahoma");   // Simple font face for small digits
		m_hfnt.CreateFontIndirect(&lf);
		lf.lfEscapement = 900;             // text at 90 degrees
		m_vfnt.CreateFontIndirect(&lf);

		m_pdc->SetBkMode(TRANSPARENT);
		m_pdc->SetTextColor(m_axes);       // use same colour as axes for now
	}

	// Get adjusted colours
	COLORREF *pc = new COLORREF[m_col.size()];
	for (int ii = 0; ii < m_col.size(); ++ii)
		pc[ii] = add_contrast(m_col[ii], m_back);

	// Draw background
	m_pdc->FillSolidRect(0, 0, m_rct.Width(), m_rct.Height(), m_back);
	draw_axes();

	int width = m_rct.Width() - m_left - m_right;     // width of the actual graph area
	int height = m_rct.Height() - m_top - m_bottom;   // height of the graph
	int ypos = m_rct.Height() - m_bottom;             // bottom of the graph (dist. from rect top)

	// Draw bars of graph
	ASSERT(m_val.size() == m_col.size());
	int xpos = m_left;
	for (int ii = 0; ii < m_val.size(); ++ii)
	{
		int next_xpos = m_left + (width*(ii+1))/m_val.size();
		int cy = int((m_val[ii]*height)/m_max);

		m_pdc->FillSolidRect(xpos, ypos - cy, next_xpos - xpos, cy, pc[ii]);        // draw bar for this byte value
		//m_pdc->FillSolidRect(xpos, ypos - cy, next_xpos - xpos,  1, RGB(0, 0, 0));  // add black tip to all bars

		// Add ticks
		if (ii % 16 == 0)
		{
			int siz;
			if (ii %64 == 0)
				siz = 4;
			else
				siz = 2;
			m_pdc->FillSolidRect(xpos - 1, ypos + 1, 1, siz, m_axes);
		}
		xpos = next_xpos;
	}
	m_pdc->FillSolidRect(xpos - 1, ypos + 1, 1, 4, m_axes);   // Right end tick
	delete[] pc;

	Invalidate();
}

void CSimpleGraph::draw_axes()
{
	int width = m_rct.Width() - m_left - m_right;
	int height = m_rct.Height() - m_top - m_bottom;

	m_pdc->FillSolidRect(m_left - 1, m_top - 1, 1, height + 1, m_axes);      // vert axis

	// Calculate tick interval, units etc for the vertical axis
	int max;                         // scaled max value
	CString ustr;                    // scaling unit
	if (m_max >= 2000000000)
	{
		max = int(m_max/1000000000);
		ustr = "Billions";
	}
	else if (m_max >= 2000000)
	{
		max = int(m_max/1000000);
		ustr = "Millions";
	}
	else if (m_max >= 2000)
	{
		max = int(m_max/1000);
		ustr = "Thousands";
	}
	else
	{
		max = int(m_max);
		ustr = "";
	}

	int max_ticks = height/20;              // leave at least 20 pixels between ticks
	int min_spacing = max/max_ticks + 1;    // min tick distance in graph units

	char buf[10];
	sprintf(buf, "%d", min_spacing);
	double mag = pow(10.0, double(strlen(buf) - 1));   // max magnitude
	double vv = min_spacing/mag;

	int tick;                              // how far apart are ticks in scaled units
	if (vv > 5.0)
		tick = 10 * int(mag);
	else if (vv > 2.0)
		tick = 5 * int(mag);
	else if (vv > 1.0)
		tick = 2 * int(mag);
	else
		tick = int(mag);

	CRect rct;
	m_pdc->SelectObject(&m_hfnt);
	for (int tt = 0; tt < max; tt += tick)
	{
		int ypos = m_rct.Height() - m_bottom - (tt*height)/max;    // xxx is this right as max is rounded??
		m_pdc->FillSolidRect(m_left - 3, ypos, 3, 1, m_axes);

		// Draw units next to tick
		//if (tt < (max*7)/8)        // don't draw near the top so we have room for the "Show" button
		{
			sprintf(buf, "%d", tt);
			rct.left = 0;
			rct.right = 28;
			rct.bottom = ypos + 7;
			rct.top = rct.bottom - 20;
			m_pdc->DrawText(buf, strlen(buf), rct, DT_SINGLELINE | DT_BOTTOM | DT_RIGHT | DT_NOCLIP);
			//m_pdc->ExtTextOut(14, ypos-7, 0, NULL, buf, strlen(buf), NULL);
		}
	}

	// Vertical unit scaling text
	m_pdc->SelectObject(&m_vfnt);
	m_pdc->ExtTextOutA(0, m_rct.Height() - m_bottom, 0, NULL, ustr, strlen(ustr), NULL);

	// Draw horizontal axis
	m_pdc->FillSolidRect(m_left - 1, m_rct.Height() - m_bottom, width + 1, 1, m_axes);
}

void CSimpleGraph::track_mouse(unsigned long flag)
{
	TRACKMOUSEEVENT tme;
	tme.cbSize = sizeof(tme);
	tme.dwFlags = flag;
	tme.dwHoverTime = 0;
	tme.hwndTrack = m_hWnd;

	VERIFY(::_TrackMouseEvent(&tme));
}

int CSimpleGraph::get_bar(CPoint pt)
{
	++pt.x;  // mouse posn seems to be out by 1 pixel
	if (pt.y <= m_top || pt.y >= m_rct.Height() - m_bottom ||
		pt.x < m_left || pt.x > m_rct.Width() - m_right)
	{
		// Outside graph area so return
		return -1;
	}

	int retval = ((pt.x - m_left)*m_val.size())/(m_rct.Width() - m_left - m_right);
	if (retval >= m_val.size())
		retval = m_val.size() - 1;

	return retval;
}

BEGIN_MESSAGE_MAP(CSimpleGraph, CWnd)
	ON_WM_PAINT()
	ON_WM_SIZE()
	ON_WM_SETCURSOR()
	ON_WM_MOUSEMOVE()
	ON_MESSAGE(WM_MOUSEHOVER, OnMouseHover)
	ON_MESSAGE(WM_MOUSELEAVE, OnMouseLeave)
END_MESSAGE_MAP()

// CSimpleGraph message handlers
void CSimpleGraph::OnPaint()
{
	CPaintDC dc(this); // device context for painting

	CRect rct;
	GetClientRect(rct);

	// Just BLT the current graph into the display
	dc.BitBlt(rct.left, rct.top, rct.Width(), rct.Height(), m_pdc, 0, 0, SRCCOPY);
}

void CSimpleGraph::OnSize(UINT nType, int cx, int cy)
{
	__super::OnSize(nType, cx, cy);
	update();
}

BOOL CSimpleGraph::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
{
	CPoint pt;
	::GetCursorPos(&pt);            // get mouse location (screen coords)
	ScreenToClient(&pt);
	if (get_bar(pt) > -1)
		::SetCursor(theApp.LoadCursor(IDC_INFO));
	else
		::SetCursor(AfxGetApp()->LoadStandardCursor(IDC_ARROW));
	return TRUE;
}

void CSimpleGraph::OnMouseMove(UINT nFlags, CPoint point)
{
	CWnd::OnMouseMove(nFlags, point);

	// Check for hover if no tip window is visible
	if (!m_tip.IsWindowVisible())
		track_mouse(TME_HOVER);
	else if (get_bar(point) != m_bar && !m_tip.FadingOut())
		m_tip.Hide(300);
}

LRESULT CSimpleGraph::OnMouseHover(WPARAM, LPARAM lp)
{
	CPoint pt(LOWORD(lp), HIWORD(lp));  // client window coords

	m_bar = get_bar(pt);
	if (m_bar > -1 && !m_tip.IsWindowVisible())
	{
		// Update the tip info
		m_tip.Clear();
		CString ss;
		if (theApp.hex_ucase_)
			ss.Format("Byte: %d [%02.2Xh]", m_bar, m_bar);
		else
			ss.Format("Byte: %d [%02.2xh]", m_bar, m_bar);
		m_tip.AddString(ss);

		char buf[32];                   // used with sprintf (CString::Format can't handle __int64)
		sprintf(buf, "%I64d", m_val[m_bar]);
		ss = buf;
		AddCommas(ss);
		m_tip.AddString("Count: " +ss);

		// Work out the tip window display position and move the tip window there
		CPoint tip_pt = pt + CSize(16, 16);
		ClientToScreen(&tip_pt);
		m_tip.Move(tip_pt, false);

		m_tip.Show();
		track_mouse(TME_LEAVE);
		return 0;
	}

	return 1;  // return 0 to say we processed it
}

LRESULT CSimpleGraph::OnMouseLeave(WPARAM, LPARAM lp)
{
	m_tip.Hide(300);
	return 0;
}
<commit_msg>Avoid divide by zero when graph height is squashed to nothing.<commit_after>// SimpleGraph.cpp : implementation file
//

#include "stdafx.h"
#include "SimpleGraph.h"
#include "misc.h"

// CSimpleGraph

IMPLEMENT_DYNAMIC(CSimpleGraph, CWnd)

CSimpleGraph::CSimpleGraph()
{
	m_pdc = NULL;
	m_bar = -1;
}

void CSimpleGraph::SetData(FILE_ADDRESS maximum, std::vector<FILE_ADDRESS> val, std::vector<COLORREF> col)
{
	if (val == m_val && col == m_col)
		return;                         // no change

	ASSERT(val.size() == col.size());
	m_max = maximum;
	m_val.swap(val);
	m_col.swap(col);

	update();
}

void CSimpleGraph::update()
{
	CRect rct;
	GetClientRect(rct);

	// If the graph has been resized we need a new DC/bitmap
	if (m_rct != rct)
	{
		m_rct = rct;

		// Delete old DC/bitmap
		if (m_pdc != NULL)
		{
			delete m_pdc;
			m_pdc = NULL;
			m_bm.DeleteObject();
			m_hfnt.DeleteObject();
			m_vfnt.DeleteObject();
		}

		// Recreate DC/bitmap for drawing into
		CDC* pDC = GetDC();
		m_pdc = new CDC();
		m_pdc->CreateCompatibleDC(pDC);
		m_bm.CreateCompatibleBitmap(pDC, rct.Width(), rct.Height());
		m_pdc->SelectObject(&m_bm);

		// Set up text including creating the font
		LOGFONT lf;
		memset(&lf, '\0', sizeof(lf));
		lf.lfHeight = 12;
		strcpy(lf.lfFaceName, "Tahoma");   // Simple font face for small digits
		m_hfnt.CreateFontIndirect(&lf);
		lf.lfEscapement = 900;             // text at 90 degrees
		m_vfnt.CreateFontIndirect(&lf);

		m_pdc->SetBkMode(TRANSPARENT);
		m_pdc->SetTextColor(m_axes);       // use same colour as axes for now
	}

	// Get adjusted colours
	COLORREF *pc = new COLORREF[m_col.size()];
	for (int ii = 0; ii < m_col.size(); ++ii)
		pc[ii] = add_contrast(m_col[ii], m_back);

	// Draw background
	m_pdc->FillSolidRect(0, 0, m_rct.Width(), m_rct.Height(), m_back);
	draw_axes();

	int width = m_rct.Width() - m_left - m_right;     // width of the actual graph area
	int height = m_rct.Height() - m_top - m_bottom;   // height of the graph
	int ypos = m_rct.Height() - m_bottom;             // bottom of the graph (dist. from rect top)

	// Draw bars of graph
	ASSERT(m_val.size() == m_col.size());
	int xpos = m_left;
	for (int ii = 0; ii < m_val.size(); ++ii)
	{
		int next_xpos = m_left + (width*(ii+1))/m_val.size();
		int cy = int((m_val[ii]*height)/m_max);

		m_pdc->FillSolidRect(xpos, ypos - cy, next_xpos - xpos, cy, pc[ii]);        // draw bar for this byte value
		//m_pdc->FillSolidRect(xpos, ypos - cy, next_xpos - xpos,  1, RGB(0, 0, 0));  // add black tip to all bars

		// Add ticks
		if (ii % 16 == 0)
		{
			int siz;
			if (ii %64 == 0)
				siz = 4;
			else
				siz = 2;
			m_pdc->FillSolidRect(xpos - 1, ypos + 1, 1, siz, m_axes);
		}
		xpos = next_xpos;
	}
	m_pdc->FillSolidRect(xpos - 1, ypos + 1, 1, 4, m_axes);   // Right end tick
	delete[] pc;

	Invalidate();
}

void CSimpleGraph::draw_axes()
{
	int width = m_rct.Width() - m_left - m_right;
	int height = m_rct.Height() - m_top - m_bottom;

	m_pdc->FillSolidRect(m_left - 1, m_top - 1, 1, height + 1, m_axes);      // vert axis

	// Calculate tick interval, units etc for the vertical axis
	int max;                         // scaled max value
	CString ustr;                    // scaling unit
	if (m_max >= 2000000000)
	{
		max = int(m_max/1000000000);
		ustr = "Billions";
	}
	else if (m_max >= 2000000)
	{
		max = int(m_max/1000000);
		ustr = "Millions";
	}
	else if (m_max >= 2000)
	{
		max = int(m_max/1000);
		ustr = "Thousands";
	}
	else
	{
		max = int(m_max);
		ustr = "";
	}

	int max_ticks = height/20;              // leave at least 20 pixels between ticks
	int min_spacing = max_ticks == 0 ? max : max/max_ticks + 1;    // min tick distance in graph units

	char buf[10];
	sprintf(buf, "%d", min_spacing);
	double mag = pow(10.0, double(strlen(buf) - 1));   // max magnitude
	double vv = min_spacing/mag;

	int tick;                              // how far apart are ticks in scaled units
	if (vv > 5.0)
		tick = 10 * int(mag);
	else if (vv > 2.0)
		tick = 5 * int(mag);
	else if (vv > 1.0)
		tick = 2 * int(mag);
	else
		tick = int(mag);

	CRect rct;
	m_pdc->SelectObject(&m_hfnt);
	for (int tt = 0; tt < max; tt += tick)
	{
		int ypos = m_rct.Height() - m_bottom - (tt*height)/max;    // xxx is this right as max is rounded??
		m_pdc->FillSolidRect(m_left - 3, ypos, 3, 1, m_axes);

		// Draw units next to tick
		//if (tt < (max*7)/8)        // don't draw near the top so we have room for the "Show" button
		{
			sprintf(buf, "%d", tt);
			rct.left = 0;
			rct.right = 28;
			rct.bottom = ypos + 7;
			rct.top = rct.bottom - 20;
			m_pdc->DrawText(buf, strlen(buf), rct, DT_SINGLELINE | DT_BOTTOM | DT_RIGHT | DT_NOCLIP);
			//m_pdc->ExtTextOut(14, ypos-7, 0, NULL, buf, strlen(buf), NULL);
		}
	}

	// Vertical unit scaling text
	m_pdc->SelectObject(&m_vfnt);
	m_pdc->ExtTextOutA(0, m_rct.Height() - m_bottom, 0, NULL, ustr, strlen(ustr), NULL);

	// Draw horizontal axis
	m_pdc->FillSolidRect(m_left - 1, m_rct.Height() - m_bottom, width + 1, 1, m_axes);
}

void CSimpleGraph::track_mouse(unsigned long flag)
{
	TRACKMOUSEEVENT tme;
	tme.cbSize = sizeof(tme);
	tme.dwFlags = flag;
	tme.dwHoverTime = 0;
	tme.hwndTrack = m_hWnd;

	VERIFY(::_TrackMouseEvent(&tme));
}

int CSimpleGraph::get_bar(CPoint pt)
{
	++pt.x;  // mouse posn seems to be out by 1 pixel
	if (pt.y <= m_top || pt.y >= m_rct.Height() - m_bottom ||
		pt.x < m_left || pt.x > m_rct.Width() - m_right)
	{
		// Outside graph area so return
		return -1;
	}

	int retval = ((pt.x - m_left)*m_val.size())/(m_rct.Width() - m_left - m_right);
	if (retval >= m_val.size())
		retval = m_val.size() - 1;

	return retval;
}

BEGIN_MESSAGE_MAP(CSimpleGraph, CWnd)
	ON_WM_PAINT()
	ON_WM_SIZE()
	ON_WM_SETCURSOR()
	ON_WM_MOUSEMOVE()
	ON_MESSAGE(WM_MOUSEHOVER, OnMouseHover)
	ON_MESSAGE(WM_MOUSELEAVE, OnMouseLeave)
END_MESSAGE_MAP()

// CSimpleGraph message handlers
void CSimpleGraph::OnPaint()
{
	CPaintDC dc(this); // device context for painting

	CRect rct;
	GetClientRect(rct);

	// Just BLT the current graph into the display
	dc.BitBlt(rct.left, rct.top, rct.Width(), rct.Height(), m_pdc, 0, 0, SRCCOPY);
}

void CSimpleGraph::OnSize(UINT nType, int cx, int cy)
{
	__super::OnSize(nType, cx, cy);
	update();
}

BOOL CSimpleGraph::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
{
	CPoint pt;
	::GetCursorPos(&pt);            // get mouse location (screen coords)
	ScreenToClient(&pt);
	if (get_bar(pt) > -1)
		::SetCursor(theApp.LoadCursor(IDC_INFO));  // show info cursor if over the graph
	else
		::SetCursor(AfxGetApp()->LoadStandardCursor(IDC_ARROW));
	return TRUE;
}

void CSimpleGraph::OnMouseMove(UINT nFlags, CPoint point)
{
	CWnd::OnMouseMove(nFlags, point);

	// Check for hover if no tip window is visible
	if (!m_tip.IsWindowVisible())
		track_mouse(TME_HOVER);
	else if (get_bar(point) != m_bar && !m_tip.FadingOut())
		m_tip.Hide(300);
}

LRESULT CSimpleGraph::OnMouseHover(WPARAM, LPARAM lp)
{
	CPoint pt(LOWORD(lp), HIWORD(lp));  // client window coords

	m_bar = get_bar(pt);
	if (m_bar > -1 && !m_tip.IsWindowVisible())
	{
		// Update the tip info
		m_tip.Clear();
		CString ss;
		if (theApp.hex_ucase_)
			ss.Format("Byte: %d [%02.2Xh]", m_bar, m_bar);
		else
			ss.Format("Byte: %d [%02.2xh]", m_bar, m_bar);
		m_tip.AddString(ss);

		char buf[32];                   // used with sprintf (CString::Format can't handle __int64)
		sprintf(buf, "%I64d", m_val[m_bar]);
		ss = buf;
		AddCommas(ss);
		m_tip.AddString("Count: " +ss);

		// Work out the tip window display position and move the tip window there
		CPoint tip_pt = pt + CSize(16, 16);
		ClientToScreen(&tip_pt);
		m_tip.Move(tip_pt, false);

		m_tip.Show();
		track_mouse(TME_LEAVE);
		return 0;  // return 0 to say we processed it
	}

	return 1;
}

LRESULT CSimpleGraph::OnMouseLeave(WPARAM, LPARAM lp)
{
	// Make sure we hide the tip window when the mouse leave the window (no more move messages are received)
	m_tip.Hide(300);
	return 0;
}
<|endoftext|>


<commit_before>#include "medButton.h"

#include <QtGui>

class medButtonPrivate
{
public:
    QLabel * icon;
};

medButton::medButton( QWidget *parent, QString resourceLocation, QString toolTip ):
    QWidget(parent), d(new medButtonPrivate)
{
    d->icon = new QLabel(this);
    d->icon->setPixmap(QPixmap(resourceLocation));

    QHBoxLayout * layout = new QHBoxLayout(this);
    layout->setContentsMargins(0, 0, 0, 0);
    layout->addWidget(d->icon);
    setToolTip(toolTip);
}

medButton::medButton( QWidget *parent, QPixmap pixmap, QString toolTip ):
    QWidget(parent),d(new medButtonPrivate)
{
    d->icon = new QLabel(this);
    d->icon->setPixmap(pixmap);

    QHBoxLayout *layout = new QHBoxLayout(this);
    layout->setContentsMargins(0, 0, 0, 0);
    layout->addWidget(d->icon);
    setToolTip(toolTip);
}

medButton::~medButton( void )
{

}

QSize medButton::sizeHint( void ) const
{
    return d->icon->size();
}

void medButton::setIcon(QPixmap icon)
{
    d->icon->setPixmap(icon);
}

void medButton::mousePressEvent( QMouseEvent *event )
{
    emit triggered();
}
<commit_msg>Fix memory leak in medButton.<commit_after>#include "medButton.h"

#include <QtGui>

class medButtonPrivate
{
public:
    QLabel * icon;
};

medButton::medButton( QWidget *parent, QString resourceLocation, QString toolTip ):
    QWidget(parent), d(new medButtonPrivate)
{
    d->icon = new QLabel(this);
    d->icon->setPixmap(QPixmap(resourceLocation));

    QHBoxLayout * layout = new QHBoxLayout(this);
    layout->setContentsMargins(0, 0, 0, 0);
    layout->addWidget(d->icon);
    setToolTip(toolTip);
}

medButton::medButton( QWidget *parent, QPixmap pixmap, QString toolTip ):
    QWidget(parent),d(new medButtonPrivate)
{
    d->icon = new QLabel(this);
    d->icon->setPixmap(pixmap);

    QHBoxLayout *layout = new QHBoxLayout(this);
    layout->setContentsMargins(0, 0, 0, 0);
    layout->addWidget(d->icon);
    setToolTip(toolTip);
}

medButton::~medButton( void )
{
    delete d;
}

QSize medButton::sizeHint( void ) const
{
    return d->icon->size();
}

void medButton::setIcon(QPixmap icon)
{
    d->icon->setPixmap(icon);
}

void medButton::mousePressEvent( QMouseEvent *event )
{
    emit triggered();
}
<|endoftext|>


<commit_before>#include "mediamodel.h"
#include "mediascanner.h"
#include "dbreader.h"
#include "backend.h"

#define DEBUG if (1) qDebug() << __PRETTY_FUNCTION__

MediaModel::MediaModel(QObject *parent)
    : QAbstractItemModel(parent), m_loading(false), m_loaded(false), m_reader(0), m_readerThread(0)
{
}

MediaModel::~MediaModel()
{
}

QString MediaModel::part() const
{
    return m_structure.split("|").value(m_cursor.length());
}

QString MediaModel::mediaType() const
{
    return m_mediaType;
}

void MediaModel::setMediaType(const QString &type)
{
    m_mediaType = type;
    emit mediaTypeChanged();

    beginResetModel();
    initialize();
    endResetModel();

    QSqlDriver *driver = Backend::instance()->mediaDatabase().driver();
    QSqlRecord record = driver->record(m_mediaType);
    if (record.isEmpty())
        qWarning() << "Table " << type << " is not valid it seems";

    QHash<int, QByteArray> hash = roleNames();

    for (int i = 0; i < record.count(); i++) {
        hash.insert(Qt::UserRole + i, record.fieldName(i).toUtf8());
    }

    setRoleNames(hash);
}

void MediaModel::addSearchPath(const QString &path, const QString &name)
{
    QMetaObject::invokeMethod(MediaScanner::instance(), "addSearchPath", Qt::QueuedConnection, Q_ARG(QString, "picture"), Q_ARG(QString, path), Q_ARG(QString, name));
}

void MediaModel::removeSearchPath(int index)
{
    Q_UNUSED(index);
}

QString MediaModel::structure() const
{
    return m_structure;
}

void MediaModel::setStructure(const QString &str)
{
    m_structure = str;
    emit structureChanged();

    beginResetModel();
    initialize();
    endResetModel();
}

void MediaModel::enter(int index)
{
    Q_UNUSED(index);

    if (m_cursor.count() + 1 == m_structure.split("|").count() &&  index != 0 /* up on leaf node is OK */) {
        DEBUG << "Refusing to enter leaf node";
        return;
    }

    if (index == 0 && !m_cursor.isEmpty()) {
        back();
        return;
    }

    DEBUG << "Entering " << index;

    m_cursor.append(m_data[index]);

    beginResetModel();
    initialize();
    endResetModel();

    emit partChanged();
}

void MediaModel::back()
{
    beginResetModel();
    m_cursor.removeLast();
    initialize();
    endResetModel();
    emit partChanged();
}

QVariant MediaModel::data(const QModelIndex &index, int role) const
{
    if (!index.isValid())
        return QVariant();

    const QHash<QString, QVariant> &data = m_data[index.row()];
    const QHash<int, QByteArray> hash = roleNames();
    return data.value(hash.value(role));
}

QModelIndex MediaModel::index(int row, int col, const QModelIndex &parent) const
{
    if (parent.isValid())
        return QModelIndex();

    return createIndex(row, col);
}

QModelIndex MediaModel::parent(const QModelIndex &idx) const
{
    Q_UNUSED(idx);
    return QModelIndex();
}

int MediaModel::rowCount(const QModelIndex &parent) const
{
    if (parent.isValid())
        return 0;
    return m_data.count();
}

int MediaModel::columnCount(const QModelIndex &idx) const
{
    Q_UNUSED(idx);
    return 1;
}

bool MediaModel::hasChildren(const QModelIndex &parent) const
{
    return !parent.isValid();
}

bool MediaModel::canFetchMore(const QModelIndex &parent) const
{
    if (parent.isValid() || m_mediaType.isEmpty() || m_structure.isEmpty())
        return false;
    return !m_loading && !m_loaded;
}

void MediaModel::fetchMore(const QModelIndex &parent)
{
    if (!canFetchMore(parent))
        return;

    initialize();

    QSqlQuery q = query();
    DEBUG << q.lastQuery();

    QMetaObject::invokeMethod(m_reader, "execute", Qt::QueuedConnection, Q_ARG(QSqlQuery, q));
}

void MediaModel::initialize()
{
    m_data.clear();

    DbReader *newReader = new DbReader;
    if (m_reader) {
        disconnect(m_reader, 0, this, 0);
        m_reader->stop();
        m_reader->deleteLater();
    }
    m_reader = newReader;

    if (!m_readerThread) {
        m_readerThread = new QThread(this);
        m_readerThread->start();
    }
    m_reader->moveToThread(m_readerThread);
    QMetaObject::invokeMethod(m_reader, "initialize", Q_ARG(QSqlDatabase, Backend::instance()->mediaDatabase()));
    connect(m_reader, SIGNAL(dataReady(DbReader *, QList<QSqlRecord>, void *)),
            this, SLOT(handleDataReady(DbReader *, QList<QSqlRecord>, void *)));

    m_loading = m_loaded = false;
}

void MediaModel::handleDataReady(DbReader *reader, const QList<QSqlRecord> &records, void *node)
{
    Q_ASSERT(reader == m_reader);
    Q_UNUSED(reader);
    Q_UNUSED(node);

    DEBUG << "Received response from db of size " << records.size();

    if (!m_cursor.isEmpty()) {
        beginInsertRows(QModelIndex(), 0, records.count());
        QHash<QString, QVariant> data;
        data.insert("display", tr(".."));
        m_data.append(data);
    } else {
        beginInsertRows(QModelIndex(), 0, records.count() - 1);
    }

    for (int i = 0; i < records.count(); i++) {
        QHash<QString, QVariant> data;
        for (int j = 0; j < records[i].count(); j++) {
            data.insert(records[i].fieldName(j), records[i].value(j));
        }

        QStringList cols = m_structure.split("|").value(m_cursor.count()).split(",");
        QStringList displayString;
        for (int j = 0; j < cols.count(); j++) {
            displayString << records[i].value(cols[j]).toString();
        }
        data.insert("display", displayString.join(", "));

        m_data.append(data);

    }

    m_loading = false;
    m_loaded = true;

    endInsertRows();
}

void MediaModel::handleDatabaseUpdated(const QList<QSqlRecord> &records)
{
    Q_UNUSED(records);
    // not implemented yet
}

// ## BIG FAT FIXME: Fix the string escaping to prevent sql injection!
QSqlQuery MediaModel::query()
{
    QString q;
    QStringList parts = m_structure.split("|");
    QString curPart = parts[m_cursor.count()];

    QSqlQuery query(Backend::instance()->mediaDatabase());
    query.setForwardOnly(true);

    if (parts.count() == 1) {
        query.prepare(QString("SELECT * FROM %1").arg(m_mediaType));
        return query;
    }

    QStringList where;
    for (int i = 0; i < m_cursor.count(); i++) {
        QString part = parts[i];
        QStringList subParts = part.split(",");
        for (int j = 0; j < subParts.count(); j++)
            where.append(subParts[j] + " = '" + m_cursor[i].value(subParts[j]).toString() + "'");
    }
    QString conditions = where.join(" AND ");

    if (conditions.isEmpty()) {
        query.prepare(QString("SELECT * FROM %1 GROUP BY %2").arg(m_mediaType).arg(curPart));
    } else {
        query.prepare(QString("SELECT DISTINCT %1 FROM %2 WHERE %3").arg(curPart).arg(m_mediaType).arg(conditions));
    }

    return query;
}

<commit_msg>Add .. role<commit_after>#include "mediamodel.h"
#include "mediascanner.h"
#include "dbreader.h"
#include "backend.h"

#define DEBUG if (1) qDebug() << __PRETTY_FUNCTION__

MediaModel::MediaModel(QObject *parent)
    : QAbstractItemModel(parent), m_loading(false), m_loaded(false), m_reader(0), m_readerThread(0)
{
}

MediaModel::~MediaModel()
{
}

QString MediaModel::part() const
{
    return m_structure.split("|").value(m_cursor.length());
}

QString MediaModel::mediaType() const
{
    return m_mediaType;
}

void MediaModel::setMediaType(const QString &type)
{
    m_mediaType = type;
    emit mediaTypeChanged();

    beginResetModel();
    initialize();
    endResetModel();

    QSqlDriver *driver = Backend::instance()->mediaDatabase().driver();
    QSqlRecord record = driver->record(m_mediaType);
    if (record.isEmpty())
        qWarning() << "Table " << type << " is not valid it seems";

    QHash<int, QByteArray> hash = roleNames();
    hash.insert(Qt::UserRole, "dotdot");

    for (int i = 0; i < record.count(); i++) {
        hash.insert(Qt::UserRole + i + 1, record.fieldName(i).toUtf8());
    }

    setRoleNames(hash);
}

void MediaModel::addSearchPath(const QString &path, const QString &name)
{
    QMetaObject::invokeMethod(MediaScanner::instance(), "addSearchPath", Qt::QueuedConnection, Q_ARG(QString, "picture"), Q_ARG(QString, path), Q_ARG(QString, name));
}

void MediaModel::removeSearchPath(int index)
{
    Q_UNUSED(index);
}

QString MediaModel::structure() const
{
    return m_structure;
}

void MediaModel::setStructure(const QString &str)
{
    m_structure = str;
    emit structureChanged();

    beginResetModel();
    initialize();
    endResetModel();
}

void MediaModel::enter(int index)
{
    Q_UNUSED(index);

    if (m_cursor.count() + 1 == m_structure.split("|").count() &&  index != 0 /* up on leaf node is OK */) {
        DEBUG << "Refusing to enter leaf node";
        return;
    }

    if (index == 0 && !m_cursor.isEmpty()) {
        back();
        return;
    }

    DEBUG << "Entering " << index;

    m_cursor.append(m_data[index]);

    beginResetModel();
    initialize();
    endResetModel();

    emit partChanged();
}

void MediaModel::back()
{
    beginResetModel();
    m_cursor.removeLast();
    initialize();
    endResetModel();
    emit partChanged();
}

QVariant MediaModel::data(const QModelIndex &index, int role) const
{
    if (!index.isValid())
        return QVariant();

    const QHash<QString, QVariant> &data = m_data[index.row()];
    const QHash<int, QByteArray> hash = roleNames();
    return data.value(hash.value(role));
}

QModelIndex MediaModel::index(int row, int col, const QModelIndex &parent) const
{
    if (parent.isValid())
        return QModelIndex();

    return createIndex(row, col);
}

QModelIndex MediaModel::parent(const QModelIndex &idx) const
{
    Q_UNUSED(idx);
    return QModelIndex();
}

int MediaModel::rowCount(const QModelIndex &parent) const
{
    if (parent.isValid())
        return 0;
    return m_data.count();
}

int MediaModel::columnCount(const QModelIndex &idx) const
{
    Q_UNUSED(idx);
    return 1;
}

bool MediaModel::hasChildren(const QModelIndex &parent) const
{
    return !parent.isValid();
}

bool MediaModel::canFetchMore(const QModelIndex &parent) const
{
    if (parent.isValid() || m_mediaType.isEmpty() || m_structure.isEmpty())
        return false;
    return !m_loading && !m_loaded;
}

void MediaModel::fetchMore(const QModelIndex &parent)
{
    if (!canFetchMore(parent))
        return;

    initialize();

    QSqlQuery q = query();
    DEBUG << q.lastQuery();

    QMetaObject::invokeMethod(m_reader, "execute", Qt::QueuedConnection, Q_ARG(QSqlQuery, q));
}

void MediaModel::initialize()
{
    m_data.clear();

    DbReader *newReader = new DbReader;
    if (m_reader) {
        disconnect(m_reader, 0, this, 0);
        m_reader->stop();
        m_reader->deleteLater();
    }
    m_reader = newReader;

    if (!m_readerThread) {
        m_readerThread = new QThread(this);
        m_readerThread->start();
    }
    m_reader->moveToThread(m_readerThread);
    QMetaObject::invokeMethod(m_reader, "initialize", Q_ARG(QSqlDatabase, Backend::instance()->mediaDatabase()));
    connect(m_reader, SIGNAL(dataReady(DbReader *, QList<QSqlRecord>, void *)),
            this, SLOT(handleDataReady(DbReader *, QList<QSqlRecord>, void *)));

    m_loading = m_loaded = false;
}

void MediaModel::handleDataReady(DbReader *reader, const QList<QSqlRecord> &records, void *node)
{
    Q_ASSERT(reader == m_reader);
    Q_UNUSED(reader);
    Q_UNUSED(node);

    DEBUG << "Received response from db of size " << records.size();

    if (!m_cursor.isEmpty()) {
        beginInsertRows(QModelIndex(), 0, records.count());
        QHash<QString, QVariant> data;
        data.insert("display", tr(".."));
        data.insert("dotdot", true);
        m_data.append(data);
    } else {
        beginInsertRows(QModelIndex(), 0, records.count() - 1);
    }

    for (int i = 0; i < records.count(); i++) {
        QHash<QString, QVariant> data;
        for (int j = 0; j < records[i].count(); j++) {
            data.insert(records[i].fieldName(j), records[i].value(j));
        }

        QStringList cols = m_structure.split("|").value(m_cursor.count()).split(",");
        QStringList displayString;
        for (int j = 0; j < cols.count(); j++) {
            displayString << records[i].value(cols[j]).toString();
        }
        data.insert("display", displayString.join(", "));
        data.insert("dotdot", false);

        m_data.append(data);

    }

    m_loading = false;
    m_loaded = true;

    endInsertRows();
}

void MediaModel::handleDatabaseUpdated(const QList<QSqlRecord> &records)
{
    Q_UNUSED(records);
    // not implemented yet
}

// ## BIG FAT FIXME: Fix the string escaping to prevent sql injection!
QSqlQuery MediaModel::query()
{
    QString q;
    QStringList parts = m_structure.split("|");
    QString curPart = parts[m_cursor.count()];

    QSqlQuery query(Backend::instance()->mediaDatabase());
    query.setForwardOnly(true);

    if (parts.count() == 1) {
        query.prepare(QString("SELECT * FROM %1").arg(m_mediaType));
        return query;
    }

    QStringList where;
    for (int i = 0; i < m_cursor.count(); i++) {
        QString part = parts[i];
        QStringList subParts = part.split(",");
        for (int j = 0; j < subParts.count(); j++)
            where.append(subParts[j] + " = '" + m_cursor[i].value(subParts[j]).toString() + "'");
    }
    QString conditions = where.join(" AND ");

    if (conditions.isEmpty()) {
        query.prepare(QString("SELECT * FROM %1 GROUP BY %2").arg(m_mediaType).arg(curPart));
    } else {
        query.prepare(QString("SELECT DISTINCT %1 FROM %2 WHERE %3").arg(curPart).arg(m_mediaType).arg(conditions));
    }

    return query;
}

<|endoftext|>