<commit_before>#include "logger.h" // we implement this

#include "stream.h" // for tinfra::err
#include "trace.h"  // for TINFRA_NULL_SOURCE_LOCATION
#include "path.h"   // for basename
#include "exeinfo.h" // for get_exepath
#include "thread.h"

#ifdef _WIN32
#undef ERROR // defined by WINGDI
#endif

#include <time.h>
#include <sstream>  // for ostringstream
#include <unistd.h> //for getpid, win32 defaund yourself

namespace tinfra {

//
// log_xxx routines
//

void log_info(tstring const& m) { logger().info(m); }
void log_info(tstring const& m, tinfra::trace::location const& loc) { logger().info(m,loc); }

void log_warning(tstring const& m) { logger().warning(m); }
void log_warning(tstring const& m, tinfra::trace::location const& loc) { logger().warning(m,loc); }

void log_error(tstring const& m) { logger().error(m); }
void log_error(tstring const& m, tinfra::trace::location const& loc) { logger().error(m,loc); }

void log_fail(tstring const& m) { logger().fail(m); }
void log_fail(tstring const& m, tinfra::trace::location const& loc) { logger().fail(m,loc); }

//
// logger implementation
//

logger::logger():
    handler(log_handler::get_default()),
    component("")
{
}
logger::logger(tstring const& c):
    handler(log_handler::get_default()),
    component(c)
{
}

logger::logger(log_handler& h):
    handler(h),
    component("")
{
}

logger::logger(tstring const& c, log_handler& h):
    handler(h),
    component(c)
{
}

logger::~logger()
{
}

void logger::log(log_level level, tstring const& m, tinfra::trace::location const& loc)
{
    log_record record;
    record.level = level;
    record.component = this->component;
    record.message = m;
    ::time(&record.timestamp);
    record.location = loc;

    this->handler.log(record);
}

void logger::trace(tstring const& message)
{
    this->log(TRACE, message, TINFRA_NULL_SOURCE_LOCATION);
}

void logger::trace(tstring const& message, tinfra::trace::location const& loc)
{
    this->log(TRACE, message, loc);
}

void logger::info(tstring const& message)
{
    this->log(tinfra::INFO, message, TINFRA_NULL_SOURCE_LOCATION);
}
void logger::info(tstring const& message, tinfra::trace::location const& loc)
{
    this->log(tinfra::INFO, message, loc);
}

void logger::warning(tstring const& message)
{
    this->log(tinfra::WARNING, message, TINFRA_NULL_SOURCE_LOCATION);
}
void logger::warning(tstring const& message, tinfra::trace::location const& loc)
{
    this->log(tinfra::WARNING, message, loc);
}

void logger::error(tstring const& message)
{
    this->log(tinfra::ERROR, message, TINFRA_NULL_SOURCE_LOCATION);
}
void logger::error(tstring const& message, tinfra::trace::location const& loc)
{
    this->log(tinfra::ERROR, message, loc);
}

void logger::fail(tstring const& message)
{
    this->log(tinfra::FAIL, message, TINFRA_NULL_SOURCE_LOCATION);
}
void logger::fail(tstring const& message, tinfra::trace::location const& loc)
{
    this->log(tinfra::FAIL, message, loc);
}

static const char* log_level_to_string(log_level level)
{
    switch( level ) {
    case FATAL:   return "FATAL";
    case FAIL:    return "FAIL";
    case ERROR:   return "ERROR";
    case NOTICE:  return "NOTICE";
    case WARNING: return "WARNING";
    case INFO:    return "INFO";
    case TRACE:   return "TRACE";
    default:      return "-";
    }
}

//
// generic_log_handler
//
generic_log_handler::generic_log_handler(tinfra::output_stream& o):
    out(o)
{
}
generic_log_handler::~generic_log_handler()
{
}

static void print_log_header(std::ostream& formatter, log_record const& record)
{
    using tinfra::path::basename;
    using tinfra::get_exepath;
        // well hardcoded, but why not
    // YYYY-MM-DD HH:MM:SS name[pid] level(component::func:source:line): message

    // date
    const char* LOG_TIME_FORMAT = "%Y-%m-%d %H:%M:%S";
    {

        struct tm exploded_time;
#ifdef HAVE_LOCALTIME_R
        localtime_r(&record.timestamp, &exploded_time);
#else
        struct tm* exploded_time2 = localtime(&record.timestamp);
        exploded_time = *exploded_time2;
#endif
        char strtime_buf[256];
        strftime(strtime_buf, sizeof(strtime_buf), LOG_TIME_FORMAT, &exploded_time);
        formatter << strtime_buf << ' ';
    }

    // process name
    formatter << basename(get_exepath());

    // pid
    const bool show_pid = true;
    const bool show_tid = false;
    if( show_pid || show_tid ) {
        formatter << '[';
        if( show_pid )
            formatter << getpid();
        if( show_pid && show_tid )
            formatter << '/';
        if( show_tid )
            formatter << "tid=" << tinfra::thread::thread::current().to_number();
        formatter << "] ";
    }

    // level
    formatter << log_level_to_string(record.level);

    if( !record.component.empty() || record.location.name != 0 || record.location.filename != 0 ) {
        formatter << "(";

        // component/func/source location or -
        if( !record.component.empty() ) {
            formatter << record.component;
        }
        if( record.location.name != 0 ) {
            if( !record.component.empty() )
                formatter << "::";
            formatter << record.location.name;
        }
        if( record.location.filename != 0 ) {
            formatter << ':' << record.location.filename << ':' << record.location.line;
        }
        formatter << ')';
    }

    // message
    formatter << ": ";

}

static void print_maybe_multiline(tstring const& PS1, tstring const& PS2, tstring const& message, tinfra::output_stream& out)
{
    // TODO: implement "multiline behaviour"
    size_t start = 0;
    bool finished = false;
    do {
        if( start == message.size() ) break;

        size_t eol = message.find_first_of('\n', start);
        size_t len;
        if( eol == std::string::npos ) {
            if( start != message.size()-1 ) {
                len = std::string::npos;
                finished = true;
            } else {
                return;
            }
        } else {
            size_t pi = eol;
            if( pi > start+1 && message[eol-1] == '\r' ) --pi;
            len = pi-start;
        }
        if( len != 0 ) {
            // TODO: create tinfra string builders
            std::ostringstream tmp;
            tmp << (start == 0 ? PS1: PS2) << message.substr(start, len) << std::endl;
            std::string const stmp = tmp.str();
            out.write(stmp.data(), stmp.size());
        }
        start = eol+1;
    } while( !finished );
}

static void print_maybe_multiline(tstring const& prefix, tstring const& message, tinfra::output_stream& out)
{
    print_maybe_multiline(prefix,prefix,message,out);
}

void generic_log_handler::log(log_record const& record)
{
    std::ostringstream formatter;
    print_log_header(formatter, record);
    const std::string header = formatter.str();

    print_maybe_multiline( header, header, record.message, this->out );
}

//
// log_handler
//

static generic_log_handler cerr_log_handler(tinfra::err);

static log_handler* custom_log_handler = 0;

log_handler::~log_handler()
{
}

// singleton interface
log_handler& log_handler::get_default()
{
    if( custom_log_handler != 0 ){
        return *custom_log_handler;

    } else {
        return cerr_log_handler;
    }
}

void         log_handler::set_default(log_handler* h)
{
    custom_log_handler = h;
}

} // end namespace tinfra

<commit_msg>* logger: ported to w32<commit_after>#include "logger.h" // we implement this

#include "stream.h" // for tinfra::err
#include "trace.h"  // for TINFRA_NULL_SOURCE_LOCATION
#include "path.h"   // for basename
#include "exeinfo.h" // for get_exepath
#include "thread.h"

#ifdef _WIN32
#undef ERROR // defined by WINGDI
#endif

#ifndef HAVE_PID
#ifdef _WIN32
#include <windows.h>
#endif
#else
#include <unistd.h>
#endif

#include <time.h>
#include <sstream>  // for ostringstream

namespace tinfra {

//
// log_xxx routines
//

void log_info(tstring const& m) { logger().info(m); }
void log_info(tstring const& m, tinfra::trace::location const& loc) { logger().info(m,loc); }

void log_warning(tstring const& m) { logger().warning(m); }
void log_warning(tstring const& m, tinfra::trace::location const& loc) { logger().warning(m,loc); }

void log_error(tstring const& m) { logger().error(m); }
void log_error(tstring const& m, tinfra::trace::location const& loc) { logger().error(m,loc); }

void log_fail(tstring const& m) { logger().fail(m); }
void log_fail(tstring const& m, tinfra::trace::location const& loc) { logger().fail(m,loc); }

//
// logger implementation
//

logger::logger():
    handler(log_handler::get_default()),
    component("")
{
}
logger::logger(tstring const& c):
    handler(log_handler::get_default()),
    component(c)
{
}

logger::logger(log_handler& h):
    handler(h),
    component("")
{
}

logger::logger(tstring const& c, log_handler& h):
    handler(h),
    component(c)
{
}

logger::~logger()
{
}

void logger::log(log_level level, tstring const& m, tinfra::trace::location const& loc)
{
    log_record record;
    record.level = level;
    record.component = this->component;
    record.message = m;
    ::time(&record.timestamp);
    record.location = loc;

    this->handler.log(record);
}

void logger::trace(tstring const& message)
{
    this->log(TRACE, message, TINFRA_NULL_SOURCE_LOCATION);
}

void logger::trace(tstring const& message, tinfra::trace::location const& loc)
{
    this->log(TRACE, message, loc);
}

void logger::info(tstring const& message)
{
    this->log(tinfra::INFO, message, TINFRA_NULL_SOURCE_LOCATION);
}
void logger::info(tstring const& message, tinfra::trace::location const& loc)
{
    this->log(tinfra::INFO, message, loc);
}

void logger::warning(tstring const& message)
{
    this->log(tinfra::WARNING, message, TINFRA_NULL_SOURCE_LOCATION);
}
void logger::warning(tstring const& message, tinfra::trace::location const& loc)
{
    this->log(tinfra::WARNING, message, loc);
}

void logger::error(tstring const& message)
{
    this->log(tinfra::ERROR, message, TINFRA_NULL_SOURCE_LOCATION);
}
void logger::error(tstring const& message, tinfra::trace::location const& loc)
{
    this->log(tinfra::ERROR, message, loc);
}

void logger::fail(tstring const& message)
{
    this->log(tinfra::FAIL, message, TINFRA_NULL_SOURCE_LOCATION);
}
void logger::fail(tstring const& message, tinfra::trace::location const& loc)
{
    this->log(tinfra::FAIL, message, loc);
}

static const char* log_level_to_string(log_level level)
{
    switch( level ) {
    case FATAL:   return "FATAL";
    case FAIL:    return "FAIL";
    case ERROR:   return "ERROR";
    case NOTICE:  return "NOTICE";
    case WARNING: return "WARNING";
    case INFO:    return "INFO";
    case TRACE:   return "TRACE";
    default:      return "-";
    }
}

//
// generic_log_handler
//
generic_log_handler::generic_log_handler(tinfra::output_stream& o):
    out(o)
{
}
generic_log_handler::~generic_log_handler()
{
}
#ifndef HAVE_PID
#ifdef _WIN32
static int getpid() {
    return ::GetCurrentProcessId();
}
#endif
#else
static int getpid() { return -1; }
#endif
static void print_log_header(std::ostream& formatter, log_record const& record)
{
    using tinfra::path::basename;
    using tinfra::get_exepath;
        // well hardcoded, but why not
    // YYYY-MM-DD HH:MM:SS name[pid] level(component::func:source:line): message

    // date
    const char* LOG_TIME_FORMAT = "%Y-%m-%d %H:%M:%S";
    {

        struct tm exploded_time;
#ifdef HAVE_LOCALTIME_R
        localtime_r(&record.timestamp, &exploded_time);
#else
        struct tm* exploded_time2 = localtime(&record.timestamp);
        exploded_time = *exploded_time2;
#endif
        char strtime_buf[256];
        strftime(strtime_buf, sizeof(strtime_buf), LOG_TIME_FORMAT, &exploded_time);
        formatter << strtime_buf << ' ';
    }

    // process name
    formatter << basename(get_exepath());

    // pid
    const bool show_pid = true;
    const bool show_tid = false;
    if( show_pid || show_tid ) {
        formatter << '[';
        if( show_pid )
            formatter << getpid();
        if( show_pid && show_tid )
            formatter << '/';
        if( show_tid )
            formatter << "tid=" << tinfra::thread::thread::current().to_number();
        formatter << "] ";
    }

    // level
    formatter << log_level_to_string(record.level);

    if( !record.component.empty() || record.location.name != 0 || record.location.filename != 0 ) {
        formatter << "(";

        // component/func/source location or -
        if( !record.component.empty() ) {
            formatter << record.component;
        }
        if( record.location.name != 0 ) {
            if( !record.component.empty() )
                formatter << "::";
            formatter << record.location.name;
        }
        if( record.location.filename != 0 ) {
            formatter << ':' << record.location.filename << ':' << record.location.line;
        }
        formatter << ')';
    }

    // message
    formatter << ": ";

}

static void print_maybe_multiline(tstring const& PS1, tstring const& PS2, tstring const& message, tinfra::output_stream& out)
{
    // TODO: implement "multiline behaviour"
    size_t start = 0;
    bool finished = false;
    do {
        if( start == message.size() ) break;

        size_t eol = message.find_first_of('\n', start);
        size_t len;
        if( eol == std::string::npos ) {
            if( start != message.size()-1 ) {
                len = std::string::npos;
                finished = true;
            } else {
                return;
            }
        } else {
            size_t pi = eol;
            if( pi > start+1 && message[eol-1] == '\r' ) --pi;
            len = pi-start;
        }
        if( len != 0 ) {
            // TODO: create tinfra string builders
            std::ostringstream tmp;
            tmp << (start == 0 ? PS1: PS2) << message.substr(start, len) << std::endl;
            std::string const stmp = tmp.str();
            out.write(stmp.data(), stmp.size());
        }
        start = eol+1;
    } while( !finished );
}

static void print_maybe_multiline(tstring const& prefix, tstring const& message, tinfra::output_stream& out)
{
    print_maybe_multiline(prefix,prefix,message,out);
}

void generic_log_handler::log(log_record const& record)
{
    std::ostringstream formatter;
    print_log_header(formatter, record);
    const std::string header = formatter.str();

    print_maybe_multiline( header, header, record.message, this->out );
}

//
// log_handler
//

static generic_log_handler cerr_log_handler(tinfra::err);

static log_handler* custom_log_handler = 0;

log_handler::~log_handler()
{
}

// singleton interface
log_handler& log_handler::get_default()
{
    if( custom_log_handler != 0 ){
        return *custom_log_handler;

    } else {
        return cerr_log_handler;
    }
}

void         log_handler::set_default(log_handler* h)
{
    custom_log_handler = h;
}

} // end namespace tinfra

<|endoftext|>


<commit_before>#include "vtpath.h" // we implement this

#include "tinfra/tstring.h"
#include "tinfra/any.h"
#include "tinfra/trace.h"

#include <vector>
#include <stack>
#include <deque>
#include <iterator>
#include <string>
#include <stdexcept>

namespace tinfra {

//
// vtpath_visit
//
std::vector<const variant*> vtpath_visit(variant const& v, tstring const& expression)
{
    vtpath_visitor vtpv(const_cast<variant*>(&v), expression);

    return std::vector<const variant*>(vtpv.begin(), vtpv.end());
}

std::vector<variant*> vtpath_visit(variant& v, tstring const& expression)
{
    vtpath_visitor vtpv(&v, expression);

    return std::vector<variant*>(vtpv.begin(), vtpv.end());
}

//
// vtpath_visitor
//

enum vpath_token_type {
    ROOT,
    CURRENT_OBJECT,
    CHILD,
    RECURSIVE_CHILD,
    WILDCARD_ALL,
    WILDCARD_OTHER,
    TOKEN
};
struct vtpath_command {
    vpath_token_type type;

    variant expr;

    vtpath_command(vpath_token_type t): type(t) {}
    vtpath_command(vpath_token_type t, variant expr): type(t), expr(expr) {}
};

//
// vtpath_parse
//
tinfra::module_tracer vtpath_parse_tracer(tinfra::tinfra_tracer, "vtpath.parse");

void vtpath_parse_fail(std::string const& message)
{
    throw std::runtime_error(tsprintf("vtpath: %s", message));
}

void vtpath_parse_expect(char v, char expect, const char* message)
{
    if( v != expect ) {
        vtpath_parse_fail(tsprintf("expected '%s', but found '%s': %s", expect, v, message));
    }
}

tstring vpath_parse_selector(tstring expr, std::vector<vtpath_command>& result)
    // this function expects string is just after parsing starting [
    // it will consume ending ]
    // only [*] supported for now
{
     while( expr.size() > 0 ) {
        switch(expr[0]) {
            case '*':
                TINFRA_TRACE(vtpath_parse_tracer, "[*] -> WILDCARD_ALL");
                result.push_back(vtpath_command(WILDCARD_ALL));
                vtpath_parse_expect(expr[1],']', "* is supported only as alone token in selector");
                return expr.substr(2);
            default:
                vtpath_parse_fail("only [*] wildcard is supported for now");
                break;
        }
     }
     return expr;
}

void vtpath_parse(tstring expr, std::vector<vtpath_command>& result)
{
    while( expr.size() > 0 ) {
        switch(expr[0]) {
        case '$':
            TINFRA_TRACE(vtpath_parse_tracer, "$ -> ROOT");
            result.push_back(vtpath_command(ROOT));
            expr=expr.substr(1);
            continue;
        case '.':
            if( expr.size() > 1 && expr[1] == '.' ) {
                TINFRA_TRACE(vtpath_parse_tracer, ".. -> RECURSIVE_CHILD");
                result.push_back(vtpath_command(RECURSIVE_CHILD));
                expr=expr.substr(2);
            } else {
                TINFRA_TRACE(vtpath_parse_tracer, ". -> CHILD");
                result.push_back(vtpath_command(CHILD));
                expr=expr.substr(1);
            }
            continue;
        case '*':
            TINFRA_TRACE(vtpath_parse_tracer, "* -> WILDCARD_ALL");
            result.push_back(vtpath_command(WILDCARD_ALL));
            expr=expr.substr(1);
            continue;
        case '[':
            expr = vpath_parse_selector(expr.substr(1), result);
            continue;
        default:
            {
                std::string name;
                while( expr.size() > 0 && isalpha(expr[0]))  {
                        name += expr[0];
                        expr=expr.substr(1);
                }
                TINFRA_TRACE(vtpath_parse_tracer, name << " -> TOKEN(" << name << ")");
                result.push_back(vtpath_command(TOKEN, name));
                continue;
            }
        }
    }

}

tinfra::module_tracer vtpath_exec_tracer(tinfra::tinfra_tracer, "vtpath.exec");

struct vtpath_parse_state {
    variant*                      current;

    // iterator in container
    variant::array_type::iterator iarray;
    variant::dict_type::iterator  idict;
    int                           index;
    bool                          matching_finished;
    // iterator in expression-set
    std::vector<vtpath_command>::iterator icommand;
};

struct vtpath_visitor::internal_data {
    variant*   root;
    //std::stirng expression;

    std::vector<vtpath_command> commands;

    std::stack<vtpath_parse_state> states;

    std::deque<variant*> result_queue;
    // helper methods
    vtpath_parse_state& top() {
        return this->states.top();
    }

    // process a match
    // node, a matched node
    // command, next command to process
    void match_found(variant* node, std::vector<vtpath_command>::iterator inext_command)
    {
        if( inext_command == commands.end() ) {
            TINFRA_TRACE(vtpath_exec_tracer, "vpath_visit: matched result: " << node << ": " << *node);
            // if we're at end of pattern, i.e no rules
            // that means that we've been matched "directly"
            // so mark CURRENT as a result
            result_queue.push_back(node);
            return;
        } else {
            push_container(node, inext_command);
        }
    }
    void push_container(variant* node, std::vector<vtpath_command>::iterator inext_command)
    {
        vtpath_parse_state new_state;
        new_state.matching_finished = false;
        new_state.current  = node;
        new_state.icommand = inext_command;
        new_state.index = 0;

        if( node->is_dict() ) {
            TINFRA_TRACE(vtpath_exec_tracer, "vpath_visit: pushing dict: " << node);
            new_state.idict = node->get_dict().begin();
        } else if( node->is_array() ) {
            TINFRA_TRACE(vtpath_exec_tracer, "vpath_visit: pushing array: " << node);
            new_state.iarray = node->get_array().begin();
        } else {
            // do nothing when
            // we've found leafs not at end of
            // command chain
            return;
        }
        this->states.push(new_state);
    }
};

vtpath_visitor::vtpath_visitor(variant* v, tstring const& expression):
    self(new internal_data())
{
    self->root = v;
    vtpath_parse(expression, self->commands);
    if( self->commands.size() == 0 ) {
        vtpath_parse_fail("empty predicate");
    }
    if( self->commands[0].type != ROOT ) {
        vtpath_parse_fail("first element shall be root");
    }
    self->match_found(self->root, self->commands.begin()+1);
}
vtpath_visitor::~vtpath_visitor()
{
}
inline std::ostream& operator <<(std::ostream& s, vtpath_command const& cmd)
{
    s << '(';
    switch(cmd.type) {
    case ROOT:            s << "ROOT"; break;
    case CURRENT_OBJECT:  s << "CURRENT_OBJECT"; break;
    case CHILD:           s << "CHILD"; break;
    case RECURSIVE_CHILD: s << "RECURSIVE_CHILD"; break;
    case WILDCARD_ALL:    s << "WILDCARD_ALL"; break;
    case WILDCARD_OTHER:  s << "WILDCARD_OTHER"; break;
    case TOKEN:           s << "TOKEN"; break;
    }
    if( !cmd.expr.is_none() ) {
        s << "," << cmd.expr;
    }
    s << ')';
    return s;
}

bool vtpath_visitor::fetch_next(variant*& r)
{
    while( self->result_queue.empty() ) {
        if( self->states.empty() )
            return false;

        vtpath_parse_state& top = self->top();
        if( top.matching_finished ) {
            TINFRA_TRACE(vtpath_exec_tracer, "vpath_visit: matching finished in " << top.current);
            self->states.pop();
            continue;
        }
        bool recursive = false;
        if( top.icommand->type == CHILD ) {
            // nothing
        } else if( top.icommand->type == RECURSIVE_CHILD ) {
            recursive = true;
        } else {
            TINFRA_TRACE(vtpath_exec_tracer, "vpath_visit: state bad " << top.icommand->type << " popping state");
            self->states.pop();
            continue;
        }
        TINFRA_TRACE(vtpath_exec_tracer, "vpath_visit: node: " << (*top.current) << "(" <<top.current << ") " << *(top.icommand) << " index=" << top.index << " recursive=" << (int)recursive);

        // we still need at least one command
        TINFRA_ASSERT(top.icommand != self->commands.end()-1);
        vtpath_command const& child_match = *(top.icommand+1);
        TINFRA_TRACE(vtpath_exec_tracer, "vpath_visit: node: next_command= " << *(top.icommand+1));

        std::vector<vtpath_command>::iterator inext_command = (top.icommand+2);

        if( top.current->is_dict()) {
            variant::dict_type& dict = top.current->get_dict();
            if( child_match.type == WILDCARD_ALL || recursive) {
                // CURRENT.*, or recursive
                // in wildcard we MATCH all
                // in recursive we recurse to all
                if( top.idict != dict.end() ) {
                    variant::dict_type::iterator idict = top.idict;
                    top.idict++;
                    top.index++;

                    variant& match = idict->second;
                    TINFRA_TRACE(vtpath_exec_tracer, "vpath_visit: trying: " << top.index-1 << ": " << idict->first << " -> " << match);
                    if(        child_match.type == WILDCARD_ALL ) {
                        // CURRENT.*, matches everything
                        self->match_found(&match, inext_command);
                    } else if( child_match.type == TOKEN ) {
                        // CURRENT.TOKEN
                        if( child_match.expr.is_string() ) {
                            // CURRENT.TOKEN(string)
                            //  -> check if current matches TOKEN in dict
                            TINFRA_ASSERT(child_match.expr.is_string());
                            std::string const& token = child_match.expr.get_string();
                            if( idict->first == token ) {
                                self->match_found(&match, inext_command);
                            }
                        }
                    } else {
                        TINFRA_ASSERT(false);
                    }
                    if( recursive ) {
                        // in recursive must reapply all rules from now on all containers
                        self->push_container(&match, top.icommand);
                    }
                } else {
                    top.matching_finished = true;
                }
            } else if( child_match.type == TOKEN ) {
                // CURRENT.TOKEN, non-recursive
                //  -> search for TOKEN in dict
                std::string const& token = child_match.expr.get_string();

                variant::dict_type::iterator i = dict.find(token);
                top.matching_finished = true;
                if( i != dict.end() ) {
                    variant& match = i->second;
                    self->match_found(&match, inext_command);
                }
            }
        } else if( top.current->is_array() ) {
            variant::array_type& array = top.current->get_array();
            if( child_match.type == WILDCARD_ALL || recursive) {
                // in wildcard we MATCH all
                // in recursive we recurse to all
                if( top.iarray != array.end() ) {
                    variant::array_type::iterator iarray = top.iarray;
                    top.iarray++;
                    top.index++;

                    variant& match = *iarray;
                    TINFRA_TRACE(vtpath_exec_tracer, "vpath_visit: trying: " << top.index-1 << ": " << match);
                    if(        child_match.type == WILDCARD_ALL ) {
                        self->match_found(&match, inext_command);
                    } else if( child_match.type == TOKEN ) {
                        if( child_match.expr.is_integer()) {
                            int current_index = (iarray - array.begin());
                            int index = child_match.expr.get_integer();
                            if( index == current_index ) {
                                self->match_found(&match, inext_command);
                            }
                        }
                    } else {
                        TINFRA_ASSERT(false);
                    }

                    if( recursive ) {
                        // in recursive must reapply all rules from now on all containers
                        self->push_container(&match, top.icommand);
                    }
                } else {
                    top.matching_finished = true;
                }
            } else if( child_match.type == TOKEN ) {
                TINFRA_ASSERT(child_match.expr.is_integer());
                int index = child_match.expr.get_integer();
                top.matching_finished = true;
                if( index >= 0 && index < array.size() ) {
                    variant& match = array[index];
                    std::vector<vtpath_command>::iterator inext_command = (top.icommand+2);

                    self->match_found(&match, inext_command);
                }

            }
        } // is array
    } // while result_queue is empty
    r = self->result_queue.front();
    self->result_queue.pop_front();
    return true;
}

} // end namespace tinfra

<commit_msg>vtpath_parse: fix, word tokens parsing matches all isalnum() and underscore<commit_after>#include "vtpath.h" // we implement this

#include "tinfra/tstring.h"
#include "tinfra/any.h"
#include "tinfra/trace.h"

#include <vector>
#include <stack>
#include <deque>
#include <iterator>
#include <string>
#include <stdexcept>

namespace tinfra {

//
// vtpath_visit
//
std::vector<const variant*> vtpath_visit(variant const& v, tstring const& expression)
{
    vtpath_visitor vtpv(const_cast<variant*>(&v), expression);

    return std::vector<const variant*>(vtpv.begin(), vtpv.end());
}

std::vector<variant*> vtpath_visit(variant& v, tstring const& expression)
{
    vtpath_visitor vtpv(&v, expression);

    return std::vector<variant*>(vtpv.begin(), vtpv.end());
}

//
// vtpath_visitor
//

enum vpath_token_type {
    ROOT,
    CURRENT_OBJECT,
    CHILD,
    RECURSIVE_CHILD,
    WILDCARD_ALL,
    WILDCARD_OTHER,
    TOKEN
};
struct vtpath_command {
    vpath_token_type type;

    variant expr;

    vtpath_command(vpath_token_type t): type(t) {}
    vtpath_command(vpath_token_type t, variant expr): type(t), expr(expr) {}
};

//
// vtpath_parse
//
tinfra::module_tracer vtpath_parse_tracer(tinfra::tinfra_tracer, "vtpath.parse");

void vtpath_parse_fail(std::string const& message)
{
    throw std::runtime_error(tsprintf("vtpath: %s", message));
}

void vtpath_parse_expect(char v, char expect, const char* message)
{
    if( v != expect ) {
        vtpath_parse_fail(tsprintf("expected '%s', but found '%s': %s", expect, v, message));
    }
}

tstring vpath_parse_selector(tstring expr, std::vector<vtpath_command>& result)
    // this function expects string is just after parsing starting [
    // it will consume ending ]
    // only [*] supported for now
{
     while( expr.size() > 0 ) {
        switch(expr[0]) {
            case '*':
                TINFRA_TRACE(vtpath_parse_tracer, "[*] -> WILDCARD_ALL");
                result.push_back(vtpath_command(WILDCARD_ALL));
                vtpath_parse_expect(expr[1],']', "* is supported only as alone token in selector");
                return expr.substr(2);
            default:
                vtpath_parse_fail("only [*] wildcard is supported for now");
                break;
        }
     }
     return expr;
}

void vtpath_parse(tstring expr, std::vector<vtpath_command>& result)
{
    while( expr.size() > 0 ) {
        switch(expr[0]) {
        case '$':
            TINFRA_TRACE(vtpath_parse_tracer, "$ -> ROOT");
            result.push_back(vtpath_command(ROOT));
            expr=expr.substr(1);
            continue;
        case '.':
            if( expr.size() > 1 && expr[1] == '.' ) {
                TINFRA_TRACE(vtpath_parse_tracer, ".. -> RECURSIVE_CHILD");
                result.push_back(vtpath_command(RECURSIVE_CHILD));
                expr=expr.substr(2);
            } else {
                TINFRA_TRACE(vtpath_parse_tracer, ". -> CHILD");
                result.push_back(vtpath_command(CHILD));
                expr=expr.substr(1);
            }
            continue;
        case '*':
            TINFRA_TRACE(vtpath_parse_tracer, "* -> WILDCARD_ALL");
            result.push_back(vtpath_command(WILDCARD_ALL));
            expr=expr.substr(1);
            continue;
        case '[':
            expr = vpath_parse_selector(expr.substr(1), result);
            continue;
        default:
            {
                std::string name;
                while( expr.size() > 0 && (isalnum(expr[0]) || expr[0] == '_'))  {
                        name += expr[0];
                        expr=expr.substr(1);
                }
                TINFRA_TRACE(vtpath_parse_tracer, name << " -> TOKEN(" << name << ")");
                result.push_back(vtpath_command(TOKEN, name));
                continue;
            }
        }
    }

}

tinfra::module_tracer vtpath_exec_tracer(tinfra::tinfra_tracer, "vtpath.exec");

struct vtpath_parse_state {
    variant*                      current;

    // iterator in container
    variant::array_type::iterator iarray;
    variant::dict_type::iterator  idict;
    int                           index;
    bool                          matching_finished;
    // iterator in expression-set
    std::vector<vtpath_command>::iterator icommand;
};

struct vtpath_visitor::internal_data {
    variant*   root;
    //std::stirng expression;

    std::vector<vtpath_command> commands;

    std::stack<vtpath_parse_state> states;

    std::deque<variant*> result_queue;
    // helper methods
    vtpath_parse_state& top() {
        return this->states.top();
    }

    // process a match
    // node, a matched node
    // command, next command to process
    void match_found(variant* node, std::vector<vtpath_command>::iterator inext_command)
    {
        if( inext_command == commands.end() ) {
            TINFRA_TRACE(vtpath_exec_tracer, "vpath_visit: matched result: " << node << ": " << *node);
            // if we're at end of pattern, i.e no rules
            // that means that we've been matched "directly"
            // so mark CURRENT as a result
            result_queue.push_back(node);
            return;
        } else {
            push_container(node, inext_command);
        }
    }
    void push_container(variant* node, std::vector<vtpath_command>::iterator inext_command)
    {
        vtpath_parse_state new_state;
        new_state.matching_finished = false;
        new_state.current  = node;
        new_state.icommand = inext_command;
        new_state.index = 0;

        if( node->is_dict() ) {
            TINFRA_TRACE(vtpath_exec_tracer, "vpath_visit: pushing dict: " << node);
            new_state.idict = node->get_dict().begin();
        } else if( node->is_array() ) {
            TINFRA_TRACE(vtpath_exec_tracer, "vpath_visit: pushing array: " << node);
            new_state.iarray = node->get_array().begin();
        } else {
            // do nothing when
            // we've found leafs not at end of
            // command chain
            return;
        }
        this->states.push(new_state);
    }
};

vtpath_visitor::vtpath_visitor(variant* v, tstring const& expression):
    self(new internal_data())
{
    self->root = v;
    vtpath_parse(expression, self->commands);
    if( self->commands.size() == 0 ) {
        vtpath_parse_fail("empty predicate");
    }
    if( self->commands[0].type != ROOT ) {
        vtpath_parse_fail("first element shall be root");
    }
    self->match_found(self->root, self->commands.begin()+1);
}
vtpath_visitor::~vtpath_visitor()
{
}
inline std::ostream& operator <<(std::ostream& s, vtpath_command const& cmd)
{
    s << '(';
    switch(cmd.type) {
    case ROOT:            s << "ROOT"; break;
    case CURRENT_OBJECT:  s << "CURRENT_OBJECT"; break;
    case CHILD:           s << "CHILD"; break;
    case RECURSIVE_CHILD: s << "RECURSIVE_CHILD"; break;
    case WILDCARD_ALL:    s << "WILDCARD_ALL"; break;
    case WILDCARD_OTHER:  s << "WILDCARD_OTHER"; break;
    case TOKEN:           s << "TOKEN"; break;
    }
    if( !cmd.expr.is_none() ) {
        s << "," << cmd.expr;
    }
    s << ')';
    return s;
}

bool vtpath_visitor::fetch_next(variant*& r)
{
    while( self->result_queue.empty() ) {
        if( self->states.empty() )
            return false;

        vtpath_parse_state& top = self->top();
        if( top.matching_finished ) {
            TINFRA_TRACE(vtpath_exec_tracer, "vpath_visit: matching finished in " << top.current);
            self->states.pop();
            continue;
        }
        bool recursive = false;
        if( top.icommand->type == CHILD ) {
            // nothing
        } else if( top.icommand->type == RECURSIVE_CHILD ) {
            recursive = true;
        } else {
            TINFRA_TRACE(vtpath_exec_tracer, "vpath_visit: state bad " << top.icommand->type << " popping state");
            self->states.pop();
            continue;
        }
        TINFRA_TRACE(vtpath_exec_tracer, "vpath_visit: node: " << (*top.current) << "(" <<top.current << ") " << *(top.icommand) << " index=" << top.index << " recursive=" << (int)recursive);

        // we still need at least one command
        TINFRA_ASSERT(top.icommand != self->commands.end()-1);
        vtpath_command const& child_match = *(top.icommand+1);
        TINFRA_TRACE(vtpath_exec_tracer, "vpath_visit: node: next_command= " << *(top.icommand+1));

        std::vector<vtpath_command>::iterator inext_command = (top.icommand+2);

        if( top.current->is_dict()) {
            variant::dict_type& dict = top.current->get_dict();
            if( child_match.type == WILDCARD_ALL || recursive) {
                // CURRENT.*, or recursive
                // in wildcard we MATCH all
                // in recursive we recurse to all
                if( top.idict != dict.end() ) {
                    variant::dict_type::iterator idict = top.idict;
                    top.idict++;
                    top.index++;

                    variant& match = idict->second;
                    TINFRA_TRACE(vtpath_exec_tracer, "vpath_visit: trying: " << top.index-1 << ": " << idict->first << " -> " << match);
                    if(        child_match.type == WILDCARD_ALL ) {
                        // CURRENT.*, matches everything
                        self->match_found(&match, inext_command);
                    } else if( child_match.type == TOKEN ) {
                        // CURRENT.TOKEN
                        if( child_match.expr.is_string() ) {
                            // CURRENT.TOKEN(string)
                            //  -> check if current matches TOKEN in dict
                            TINFRA_ASSERT(child_match.expr.is_string());
                            std::string const& token = child_match.expr.get_string();
                            if( idict->first == token ) {
                                self->match_found(&match, inext_command);
                            }
                        }
                    } else {
                        TINFRA_ASSERT(false);
                    }
                    if( recursive ) {
                        // in recursive must reapply all rules from now on all containers
                        self->push_container(&match, top.icommand);
                    }
                } else {
                    top.matching_finished = true;
                }
            } else if( child_match.type == TOKEN ) {
                // CURRENT.TOKEN, non-recursive
                //  -> search for TOKEN in dict
                std::string const& token = child_match.expr.get_string();

                variant::dict_type::iterator i = dict.find(token);
                top.matching_finished = true;
                if( i != dict.end() ) {
                    variant& match = i->second;
                    self->match_found(&match, inext_command);
                }
            }
        } else if( top.current->is_array() ) {
            variant::array_type& array = top.current->get_array();
            if( child_match.type == WILDCARD_ALL || recursive) {
                // in wildcard we MATCH all
                // in recursive we recurse to all
                if( top.iarray != array.end() ) {
                    variant::array_type::iterator iarray = top.iarray;
                    top.iarray++;
                    top.index++;

                    variant& match = *iarray;
                    TINFRA_TRACE(vtpath_exec_tracer, "vpath_visit: trying: " << top.index-1 << ": " << match);
                    if(        child_match.type == WILDCARD_ALL ) {
                        self->match_found(&match, inext_command);
                    } else if( child_match.type == TOKEN ) {
                        if( child_match.expr.is_integer()) {
                            int current_index = (iarray - array.begin());
                            int index = child_match.expr.get_integer();
                            if( index == current_index ) {
                                self->match_found(&match, inext_command);
                            }
                        }
                    } else {
                        TINFRA_ASSERT(false);
                    }

                    if( recursive ) {
                        // in recursive must reapply all rules from now on all containers
                        self->push_container(&match, top.icommand);
                    }
                } else {
                    top.matching_finished = true;
                }
            } else if( child_match.type == TOKEN ) {
                TINFRA_ASSERT(child_match.expr.is_integer());
                int index = child_match.expr.get_integer();
                top.matching_finished = true;
                if( index >= 0 && index < array.size() ) {
                    variant& match = array[index];
                    std::vector<vtpath_command>::iterator inext_command = (top.icommand+2);

                    self->match_found(&match, inext_command);
                }

            }
        } // is array
    } // while result_queue is empty
    r = self->result_queue.front();
    self->result_queue.pop_front();
    return true;
}

} // end namespace tinfra

<|endoftext|>


<commit_before>/******************************************************************************
*       SOFA, Simulation Open-Framework Architecture, version 1.0 beta 4      *
*                (c) 2006-2009 MGH, INRIA, USTL, UJF, CNRS                    *
*                                                                             *
* This library is free software; you can redistribute it and/or modify it     *
* under the terms of the GNU Lesser General Public License as published by    *
* the Free Software Foundation; either version 2.1 of the License, or (at     *
* your option) any later version.                                             *
*                                                                             *
* This library is distributed in the hope that it will be useful, but WITHOUT *
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or       *
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License *
* for more details.                                                           *
*                                                                             *
* You should have received a copy of the GNU Lesser General Public License    *
* along with this library; if not, write to the Free Software Foundation,     *
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.          *
*******************************************************************************
*                               SOFA :: Modules                               *
*                                                                             *
* Authors: The SOFA Team and external contributors (see Authors.txt)          *
*                                                                             *
* Contact information: contact@sofa-framework.org                             *
******************************************************************************/
#ifndef SOFA_COMPONENT_ENGINE_EXTRUDESURFACE_INL
#define SOFA_COMPONENT_ENGINE_EXTRUDESURFACE_INL

#if !defined(__GNUC__) || (__GNUC__ > 3 || (_GNUC__ == 3 && __GNUC_MINOR__ > 3))
#pragma once
#endif

#include <sofa/component/engine/ExtrudeSurface.h>
#include <sofa/helper/gl/template.h>
#include <sofa/helper/gl/BasicShapes.h>

namespace sofa
{

namespace component
{

namespace engine
{

using namespace sofa::helper;
using namespace sofa::defaulttype;
using namespace core::objectmodel;

template <class DataTypes>
ExtrudeSurface<DataTypes>::ExtrudeSurface()
    : f_extrusionVertices( initData (&f_extrusionVertices, "extrusionVertices", "Position coordinates of the extrusion") )
    , f_surfaceVertices( initData (&f_surfaceVertices, "surfaceVertices", "Position coordinates of the surface") )
    , f_extrusionTriangles( initData (&f_extrusionTriangles, "extrusionTriangles", "Triangles indices of the extrusion") )
    , f_surfaceTriangles( initData (&f_surfaceTriangles, "surfaceTriangles", "Indices of the triangles of the surface to extrude") )
{
    addInput(&f_surfaceTriangles);
    addInput(&f_surfaceVertices);

    addOutput(&f_extrusionVertices);
    addOutput(&f_extrusionTriangles);
}

template <class DataTypes>
void ExtrudeSurface<DataTypes>::init()
{
    BaseMeshTopology* topology = dynamic_cast<BaseMeshTopology*>(getContext()->getTopology());
    if (topology != NULL)
    {
        BaseData* parent = topology->findField("triangles");
        if (parent == NULL)
        {
            sout << "ERROR: Topology " << topology->getName() << " does not contain triangles" << sendl;
        }
    }
    else
    {
        sout << "ERROR: Topology not found. Extrusion can not be computed" << sendl;
    }

    if (!f_surfaceVertices.isSet() || !f_surfaceTriangles.isSet())
    {
        sout << "ERROR: No indices or vertices given for extrusion" << sendl;
        return;
    }

}

template <class DataTypes>
void ExtrudeSurface<DataTypes>::reinit()
{
    update();
}

template <class DataTypes>
void ExtrudeSurface<DataTypes>::update()
{
    dirty = false;

    BaseMeshTopology* topology = dynamic_cast<BaseMeshTopology*>(getContext()->getTopology());

    VecCoord* extrusionVertices = f_extrusionVertices.beginEdit();
    extrusionVertices->clear();
    helper::vector<BaseMeshTopology::Triangle>* extrusionTriangles = f_extrusionTriangles.beginEdit();
    extrusionTriangles->clear();

    const helper::vector<BaseMeshTopology::TriangleID>& surfaceTriangles = f_surfaceTriangles.getValue();
    const VecCoord& surfaceVertices = f_surfaceVertices.getValue();

    helper::vector<BaseMeshTopology::TriangleID>::const_iterator itTriangles, itTrianglesSide;

    std::map<int, int> pointMatching;
    std::map<BaseMeshTopology::Edge, bool > edgesOnBorder;
    std::set<int> pointsUsed;

    for (itTriangles=surfaceTriangles.begin() ; itTriangles != surfaceTriangles.end() ; itTriangles++)
    {
        BaseMeshTopology::Triangle triangle = topology->getTriangle(*itTriangles);

        VecCoord triangleCoord;

        BaseMeshTopology::Triangle t1, t2;
        //fetch real coords
        for (unsigned int i=0 ; i<3 ; i++)
            triangleCoord.push_back(surfaceVertices[triangle[i]]);

        //compute normal
        Coord n =  cross(triangleCoord[1]-triangleCoord[0], triangleCoord[2]-triangleCoord[0]);

        //create triangle from surface and the new triangle
        //vertex created from surface has an even (2*n) index
        //vertex created from the addition with the normal has an odd (2*n + 1) index
        //a table is also used to map old vertex indices with the new set of indices
        for (unsigned int i=0 ; i<3 ; i++)
        {
            if (pointMatching.find(triangle[i]) == pointMatching.end())
            {
                extrusionVertices->push_back(surfaceVertices[triangle[i]]);
                extrusionVertices->push_back(surfaceVertices[triangle[i]] + n);

                pointMatching[triangle[i]] = extrusionVertices->size() - 2;

                t1[i] = extrusionVertices->size()-2;
                t2[i] = extrusionVertices->size()-1;
            }
            else
            {
                t1[i] = pointMatching[triangle[i]];
                t2[i] = pointMatching[triangle[i]] + 1;
            }
        }
        extrusionTriangles->push_back(t1);
        extrusionTriangles->push_back(t2);

        //to get borders, we simply stock the edge and look if it is already in the table

        BaseMeshTopology::Edge e[3];
        if (t1[0] < t1[1])
        { e[0][0] = t1[0] ; e[0][1] = t1[1] ; }
        else
        { e[0][0] = t1[1] ; e[0][1] = t1[0] ; }

        if (t1[0] < t1[2])
        { e[1][0] = t1[0] ; e[1][1] = t1[2] ; }
        else
        { e[1][0] = t1[2] ; e[1][1] = t1[0] ; }

        if (t1[1] < t1[2])
        { e[2][0] = t1[1] ; e[2][1] = t1[2] ; }
        else
        { e[2][1] = t1[1] ; e[2][0] = t1[2] ; }

        for (unsigned int i=0 ; i<3 ; i++)
        {
            if ( edgesOnBorder.find(e[i])  == edgesOnBorder.end())
                edgesOnBorder[e[i]] = true;
            else
                edgesOnBorder[e[i]] = false;
        }
    }

    std::map<BaseMeshTopology::Edge, bool >::const_iterator itEdges;
    for (itEdges = edgesOnBorder.begin() ; itEdges != edgesOnBorder.end() ; itEdges++)
    {
        //for each edge, we can get the "mirrored one" and construct 2 other triangles
        if ((*itEdges).second)
        {
            BaseMeshTopology::Edge e = (*itEdges).first;

            //first triangle
            BaseMeshTopology::Triangle ft1;
            ft1[0] = e[0];
            ft1[1] = e[1];
            ft1[2] = e[0] + 1;

            //second triangle
            BaseMeshTopology::Triangle ft2;
            ft2[0] = e[0] + 1;
            ft2[1] = e[1] + 1;
            ft2[2] = e[1];

            extrusionTriangles->push_back(ft1);
            extrusionTriangles->push_back(ft2);
        }
    }

    f_extrusionTriangles.endEdit();
    f_extrusionVertices.endEdit();
}

template <class DataTypes>
void ExtrudeSurface<DataTypes>::draw()
{

    const helper::vector<BaseMeshTopology::TriangleID> &surfaceTriangles = f_surfaceTriangles.getValue();

    helper::vector<BaseMeshTopology::TriangleID>::const_iterator itTriangles;
    glDisable(GL_LIGHTING);

    if (!this->getContext()->getShowBehaviorModels())
        return;

    if (this->getContext()->getShowWireFrame())
        glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);

    const helper::vector<BaseMeshTopology::Triangle> &extrusionTriangles = f_extrusionTriangles.getValue();
    const VecCoord& extrusionVertices = f_extrusionVertices.getValue();
    helper::vector<BaseMeshTopology::Triangle>::const_iterator it;

    //Triangles From Surface

    glColor3f(1.0,0.0,0.0);
    glBegin(GL_TRIANGLES);
    for (unsigned int i=0 ; i<surfaceTriangles.size()*2 ; i+=2)
    {
        BaseMeshTopology::Triangle triangle = extrusionTriangles[i];

        for (unsigned int j=0 ; j<3 ; j++)
        {
            Coord p = (extrusionVertices[triangle[j]]);
            glVertex3f(p[0], p[1], p[2]);
        }
    }
    glEnd();

    //Triangles From Extrusion
    glColor3f(0.0,1.0,0.0);
    glBegin(GL_TRIANGLES);
    for (unsigned int i=1 ; i<surfaceTriangles.size()*2 ; i+=2)
    {
        BaseMeshTopology::Triangle triangle = extrusionTriangles[i];

        for (unsigned int j=0 ; j<3 ; j++)
        {
            Coord p = (extrusionVertices[triangle[j]]);
            glVertex3f(p[0], p[1], p[2]);
        }
    }
    glEnd();

    //Border Triangles
    glColor3f(0.0,0.0,1.0);
    glBegin(GL_TRIANGLES);
    for (unsigned int i=surfaceTriangles.size()*2 ; i<extrusionTriangles.size() ; i++)
    {
        BaseMeshTopology::Triangle triangle = extrusionTriangles[i];

        for (unsigned int j=0 ; j<3 ; j++)
        {
            Coord p = (extrusionVertices[triangle[j]]);
            glVertex3f(p[0], p[1], p[2]);
        }
    }
    glEnd();

    if (this->getContext()->getShowWireFrame())
        glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);

    glEnable(GL_LIGHTING);
}

} // namespace engine

} // namespace component

} // namespace sofa

#endif
<commit_msg>r4838/sofa-dev : UPDATE/FIX : computation of the normal for the extruded surface<commit_after>/******************************************************************************
*       SOFA, Simulation Open-Framework Architecture, version 1.0 beta 4      *
*                (c) 2006-2009 MGH, INRIA, USTL, UJF, CNRS                    *
*                                                                             *
* This library is free software; you can redistribute it and/or modify it     *
* under the terms of the GNU Lesser General Public License as published by    *
* the Free Software Foundation; either version 2.1 of the License, or (at     *
* your option) any later version.                                             *
*                                                                             *
* This library is distributed in the hope that it will be useful, but WITHOUT *
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or       *
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License *
* for more details.                                                           *
*                                                                             *
* You should have received a copy of the GNU Lesser General Public License    *
* along with this library; if not, write to the Free Software Foundation,     *
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.          *
*******************************************************************************
*                               SOFA :: Modules                               *
*                                                                             *
* Authors: The SOFA Team and external contributors (see Authors.txt)          *
*                                                                             *
* Contact information: contact@sofa-framework.org                             *
******************************************************************************/
#ifndef SOFA_COMPONENT_ENGINE_EXTRUDESURFACE_INL
#define SOFA_COMPONENT_ENGINE_EXTRUDESURFACE_INL

#if !defined(__GNUC__) || (__GNUC__ > 3 || (_GNUC__ == 3 && __GNUC_MINOR__ > 3))
#pragma once
#endif

#include <sofa/component/engine/ExtrudeSurface.h>
#include <sofa/helper/gl/template.h>
#include <sofa/helper/gl/BasicShapes.h>

namespace sofa
{

namespace component
{

namespace engine
{

using namespace sofa::helper;
using namespace sofa::defaulttype;
using namespace core::objectmodel;

template <class DataTypes>
ExtrudeSurface<DataTypes>::ExtrudeSurface()
    : f_extrusionVertices( initData (&f_extrusionVertices, "extrusionVertices", "Position coordinates of the extrusion") )
    , f_surfaceVertices( initData (&f_surfaceVertices, "surfaceVertices", "Position coordinates of the surface") )
    , f_extrusionTriangles( initData (&f_extrusionTriangles, "extrusionTriangles", "Triangles indices of the extrusion") )
    , f_surfaceTriangles( initData (&f_surfaceTriangles, "surfaceTriangles", "Indices of the triangles of the surface to extrude") )
{
    addInput(&f_surfaceTriangles);
    addInput(&f_surfaceVertices);

    addOutput(&f_extrusionVertices);
    addOutput(&f_extrusionTriangles);
}

template <class DataTypes>
void ExtrudeSurface<DataTypes>::init()
{
    BaseMeshTopology* topology = dynamic_cast<BaseMeshTopology*>(getContext()->getTopology());
    if (topology != NULL)
    {
        BaseData* parent = topology->findField("triangles");
        if (parent == NULL)
        {
            sout << "ERROR: Topology " << topology->getName() << " does not contain triangles" << sendl;
        }
    }
    else
    {
        sout << "ERROR: Topology not found. Extrusion can not be computed" << sendl;
    }

    if (!f_surfaceVertices.isSet() || !f_surfaceTriangles.isSet())
    {
        sout << "ERROR: No indices or vertices given for extrusion" << sendl;
        return;
    }

}

template <class DataTypes>
void ExtrudeSurface<DataTypes>::reinit()
{
    update();
}

template <class DataTypes>
void ExtrudeSurface<DataTypes>::update()
{
    dirty = false;

    BaseMeshTopology* topology = dynamic_cast<BaseMeshTopology*>(getContext()->getTopology());

    VecCoord* extrusionVertices = f_extrusionVertices.beginEdit();
    extrusionVertices->clear();
    helper::vector<BaseMeshTopology::Triangle>* extrusionTriangles = f_extrusionTriangles.beginEdit();
    extrusionTriangles->clear();

    const helper::vector<BaseMeshTopology::TriangleID>& surfaceTriangles = f_surfaceTriangles.getValue();
    const VecCoord& surfaceVertices = f_surfaceVertices.getValue();

    helper::vector<BaseMeshTopology::TriangleID>::const_iterator itTriangles, itTrianglesSide;

    std::map<int, int> pointMatching;
    std::map<BaseMeshTopology::Edge, bool > edgesOnBorder;
    std::set<int> pointsUsed;
    std::map<int, std::pair<Vec3, unsigned int> > normals;

    //first loop to compute normals per point
    for (itTriangles=surfaceTriangles.begin() ; itTriangles != surfaceTriangles.end() ; itTriangles++)
    {
        BaseMeshTopology::Triangle triangle = topology->getTriangle(*itTriangles);
        VecCoord triangleCoord;

        //fetch real coords
        for (unsigned int i=0 ; i<3 ; i++)
            triangleCoord.push_back(surfaceVertices[triangle[i]]);

        //compute normal
        Coord n =  cross(triangleCoord[1]-triangleCoord[0], triangleCoord[2]-triangleCoord[0]);
        for (unsigned int i=0 ; i<3 ; i++)
        {
            normals[triangle[i]].first += n;
            normals[triangle[i]].second++;
        }
    }

    //average normals
    typename std::map<int, std::pair<Vec3, unsigned int> >::iterator itNormals;
    for (itNormals = normals.begin(); itNormals != normals.end() ; itNormals++)
        (*itNormals).second.first /= (*itNormals).second.second;

    for (itTriangles=surfaceTriangles.begin() ; itTriangles != surfaceTriangles.end() ; itTriangles++)
    {
        BaseMeshTopology::Triangle triangle = topology->getTriangle(*itTriangles);
        BaseMeshTopology::Triangle t1, t2;

        //create triangle from surface and the new triangle
        //vertex created from surface has an even (2*n) index
        //vertex created from the addition with the normal has an odd (2*n + 1) index
        //a table is also used to map old vertex indices with the new set of indices
        for (unsigned int i=0 ; i<3 ; i++)
        {
            if (pointMatching.find(triangle[i]) == pointMatching.end())
            {
                extrusionVertices->push_back(surfaceVertices[triangle[i]]);
                extrusionVertices->push_back(surfaceVertices[triangle[i]] + normals[triangle[i]].first);

                pointMatching[triangle[i]] = extrusionVertices->size() - 2;

                t1[i] = extrusionVertices->size()-2;
                t2[i] = extrusionVertices->size()-1;
            }
            else
            {
                t1[i] = pointMatching[triangle[i]];
                t2[i] = pointMatching[triangle[i]] + 1;
            }
        }
        extrusionTriangles->push_back(t1);
        extrusionTriangles->push_back(t2);

        //to get borders, we simply stock the edge and look if it is already in the table

        BaseMeshTopology::Edge e[3];
        if (t1[0] < t1[1])
        { e[0][0] = t1[0] ; e[0][1] = t1[1] ; }
        else
        { e[0][0] = t1[1] ; e[0][1] = t1[0] ; }

        if (t1[0] < t1[2])
        { e[1][0] = t1[0] ; e[1][1] = t1[2] ; }
        else
        { e[1][0] = t1[2] ; e[1][1] = t1[0] ; }

        if (t1[1] < t1[2])
        { e[2][0] = t1[1] ; e[2][1] = t1[2] ; }
        else
        { e[2][1] = t1[1] ; e[2][0] = t1[2] ; }

        for (unsigned int i=0 ; i<3 ; i++)
        {
            if ( edgesOnBorder.find(e[i])  == edgesOnBorder.end())
                edgesOnBorder[e[i]] = true;
            else
                edgesOnBorder[e[i]] = false;
        }
    }

    std::map<BaseMeshTopology::Edge, bool >::const_iterator itEdges;
    for (itEdges = edgesOnBorder.begin() ; itEdges != edgesOnBorder.end() ; itEdges++)
    {
        //for each edge, we can get the "mirrored one" and construct 2 other triangles
        if ((*itEdges).second)
        {
            BaseMeshTopology::Edge e = (*itEdges).first;

            //first triangle
            BaseMeshTopology::Triangle ft1;
            ft1[0] = e[0];
            ft1[1] = e[1];
            ft1[2] = e[0] + 1;

            //second triangle
            BaseMeshTopology::Triangle ft2;
            ft2[0] = e[0] + 1;
            ft2[1] = e[1] + 1;
            ft2[2] = e[1];

            extrusionTriangles->push_back(ft1);
            extrusionTriangles->push_back(ft2);
        }
    }

    f_extrusionTriangles.endEdit();
    f_extrusionVertices.endEdit();
}

template <class DataTypes>
void ExtrudeSurface<DataTypes>::draw()
{

    const helper::vector<BaseMeshTopology::TriangleID> &surfaceTriangles = f_surfaceTriangles.getValue();

    helper::vector<BaseMeshTopology::TriangleID>::const_iterator itTriangles;
    glDisable(GL_LIGHTING);

    if (!this->getContext()->getShowBehaviorModels())
        return;

    if (this->getContext()->getShowWireFrame())
        glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);

    const helper::vector<BaseMeshTopology::Triangle> &extrusionTriangles = f_extrusionTriangles.getValue();
    const VecCoord& extrusionVertices = f_extrusionVertices.getValue();
    helper::vector<BaseMeshTopology::Triangle>::const_iterator it;

    //Triangles From Surface

    glColor3f(1.0,0.0,0.0);
    glBegin(GL_TRIANGLES);
    for (unsigned int i=0 ; i<surfaceTriangles.size()*2 ; i+=2)
    {
        BaseMeshTopology::Triangle triangle = extrusionTriangles[i];

        for (unsigned int j=0 ; j<3 ; j++)
        {
            Coord p = (extrusionVertices[triangle[j]]);
            glVertex3f(p[0], p[1], p[2]);
        }
    }
    glEnd();

    //Triangles From Extrusion
    glColor3f(0.0,1.0,0.0);
    glBegin(GL_TRIANGLES);
    for (unsigned int i=1 ; i<surfaceTriangles.size()*2 ; i+=2)
    {
        BaseMeshTopology::Triangle triangle = extrusionTriangles[i];

        for (unsigned int j=0 ; j<3 ; j++)
        {
            Coord p = (extrusionVertices[triangle[j]]);
            glVertex3f(p[0], p[1], p[2]);
        }
    }
    glEnd();

    //Border Triangles
    glColor3f(0.0,0.0,1.0);
    glBegin(GL_TRIANGLES);
    for (unsigned int i=surfaceTriangles.size()*2 ; i<extrusionTriangles.size() ; i++)
    {
        BaseMeshTopology::Triangle triangle = extrusionTriangles[i];

        for (unsigned int j=0 ; j<3 ; j++)
        {
            Coord p = (extrusionVertices[triangle[j]]);
            glVertex3f(p[0], p[1], p[2]);
        }
    }
    glEnd();

    if (this->getContext()->getShowWireFrame())
        glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);

    glEnable(GL_LIGHTING);
}

} // namespace engine

} // namespace component

} // namespace sofa

#endif
<|endoftext|>


<commit_before>/**
\file
\brief Main header file for dsb::sequence.
*/
#ifndef DSB_SEQUENCE_HPP
#define DSB_SEQUENCE_HPP

#include <cassert>
#include <iterator>
#include <memory>

#include "dsb/config.h"

namespace dsb
{

/**
\brief  Module that defines a sequence iteration abstraction, similar to
        standard iterators, but simpler.

The central class in this module is dsb::sequence::Sequence.  Please see
its documentation for more information.
*/
namespace sequence
{

/**
\brief  An interface for sequence implementations.

To create a new sequence type (e.g. to support a new kind of backing storage),
create a class which inherits and implements this interface, and pass an
object of that class to the Sequence constructor.

`ElementT` is the type of the elements in the sequence, i.e., the return type
of Next().

\see Sequence
*/
template<typename ElementT>
class ISequenceImpl
{
public:
    /// Returns whether we have reached the end of the sequence.
    virtual bool Empty() = 0;

    /**
    \brief  Returns the element currently at the front of the sequence
            and iterates one step past it.

    It is OK to assume that Empty() has, or would have, returned `false`
    before this function is called (but good practice to verify it with
    an assertion).
    */
    virtual ElementT Next() = 0;

    // Allows deletions through a base class pointer.
    virtual ~ISequenceImpl() { }
};

/**
\brief  A class that is used to iterate over a sequence of elements of
        type `ElementT`.

This class defines two functions, Empty() and Next().  The former returns
whether we have reached the end of the sequence, and the latter returns
the element at the head of the range and simultaneously iterates past it.
The idiomatic way to iterate a sequence is thus:
~~~~{.cpp}
while (!sequence.Empty()) {
    auto element = seq.Next();
    // Do stuff with element here
}
~~~~

Sequence objects fulfil the main purpose of standard iterators, but
are different from them in several ways, some of which are advantages
and others of which are drawbacks, depending on the situation:

  - A Sequence currently only allows one-way iteration.  (In that respect
    it is similar to an [input iterator](http://www.cplusplus.com/reference/iterator/InputIterator/).)

  - A Sequence is actually more like a *pair* of iterators: the beginning
    and the end of the sequence.  This makes it easier to use, because it
    is only necessary to keep track of one object instead of two.  If `b`
    is the "begin" iterator and `e` is the "end" iterator, then Empty() is
    equivalent to `b==e`, while Next() is equivalent to `*b++`.

  - Iterators are typically value types, while Sequence has reference
    semantics.  That is, if `a` is a sequence, and we set `b = a`, then
    calling either `a.Next()` or `b.Next()` will iterate both sequences
    one step.

  - The type of a Sequence only depends on the value of its elements, and
    not on the underlying container.  That is, an object of type
    `Sequence<int>` may be used to iterate over the contents of a
    `std::vector<int>`, an `std::list<int>`, or even a plain array of
    `int`s.

  - A Sequence typically has worse performance than an iterator.  This is
    related to the previous point.  Polymorphic classes are used "under the
    hood" to hide the type of the underlying container, which means that
    calls to Empty() and Next() are indirect (virtual), and that memory for
    the underlying objects has to be dynamically allocated.

The Sequence class is just a thin wrapper around a `std::shared_ptr` to
an object of type ISequenceImpl, which contains the actual implementation
of the sequence.  (This is how the type of the underlying container gets
hidden.)  To create a new sequence type it is therefore necessary to
create a class that implements the ISequenceImpl interface.

Like iterators, a Sequence object may be invalidated by changes to the
underlying storage.  The circumstances under which this does or doesn't
happen are defined by the specific ISequenceImpl implementation used.

Furthermore, if `ElementT` is a reference type (e.g. `int&`), it means that
Next() returns a reference to the value in some underlying storage.  In that
case, the lifetime of the reference is defined by the specific sequence
implementation.
*/
template<typename ElementT>
class Sequence
{
public:
    /// Constructs a new sequence with the given implementation.
    Sequence(std::shared_ptr<ISequenceImpl<ElementT>> impl
                = std::shared_ptr<ISequenceImpl<ElementT>>())
        : m_impl(impl)
    { }

    typedef ElementT ElementType;

    /// Returns whether we have reached the end of the sequence.
    bool Empty() { return !m_impl.get() || m_impl->Empty(); }

    /**
    \brief  Returns the element currently at the front of the sequence
            and iterates one step past it.

    Calling this function is only allowed if Empty() has, or would have,
    returned `false`.
    */
    ElementT Next() { return m_impl->Next(); }

private:
    std::shared_ptr<ISequenceImpl<ElementT>> m_impl;
};

/**
\brief  A sequence implementation that wraps a pair of standard iterators.

`Iterator` is the iterator type.  The sequence is valid as long as the
iterators are valid (which again depends on the type of container iterated).

\see ArraySequence, ContainerSequence
*/
template<typename Iterator>
class IteratorSequenceImpl
    : public ISequenceImpl<typename std::iterator_traits<Iterator>::reference>
{
public:
    /**
    \brief  Constructor that takes the "begin" and "end" iterators of a
            sequence.

    The two iterators must be comparable.
    */
    IteratorSequenceImpl(Iterator begin, Iterator end)
        : m_begin(begin), m_end(end)
    { }

    bool Empty() DSB_FINAL override
    {
        return m_begin == m_end;
    }

    typename std::iterator_traits<Iterator>::reference Next() DSB_FINAL override
    {
        return *(m_begin++);
    }

private:
    Iterator m_begin;
    Iterator m_end;
};

/**
\brief  Convenience function which returns a Sequence that iterates over the
        entire contents of a standard container.

The sequence is backed by an IteratorSequenceImpl.
*/
template<typename Container>
Sequence<typename std::iterator_traits<typename Container::iterator>::reference>
    ContainerSequence(Container& c)
{
    typedef typename Container::iterator I;
    typedef typename std::iterator_traits<I>::reference E;
    return Sequence<E>(std::make_shared<IteratorSequenceImpl<I>>(
        c.begin(), c.end()));
}

// Same as the above, but for const containers.
template<typename Container>
Sequence<typename std::iterator_traits<typename Container::const_iterator>::reference>
    ContainerSequence(const Container& c)
{
    typedef typename Container::const_iterator I;
    typedef typename std::iterator_traits<I>::reference E;
    return Sequence<E>(std::make_shared<IteratorSequenceImpl<I>>(
        c.cbegin(), c.cend()));
}

/**
\brief  Convenience function which returns a Sequence that iterates over the
        entire contents of an array.

The sequence is backed by an IteratorSequenceImpl.
*/
template<typename ElementT>
Sequence<ElementT&> ArraySequence(ElementT* pointer, size_t length)
{
    return Sequence<ElementT&>(std::make_shared<IteratorSequenceImpl<ElementT*>>(
        pointer, pointer + length));
}

/**
\brief  A sequence implementation that allows iteration over the mapped values
        of a `std::map` or `std::unordered_map` (or any other type that has
        the same API).

The class stores and uses a pair of map iterators, so the sequence remains
valid under the same circumstances as the iterators.
*/
template<typename Map>
class MapValueSequenceImpl : public ISequenceImpl<typename Map::mapped_type&>
{
public:
    /// Constructor that takes a map object.
    MapValueSequenceImpl(Map& map)
        : m_begin(map.begin()), m_end(map.end())
    { }

    bool Empty() DSB_FINAL override
    {
        return m_begin == m_end;
    }

    typename Map::mapped_type& Next() DSB_FINAL override
    {
        return (m_begin++)->second;
    }

private:
    typename Map::iterator m_begin;
    typename Map::iterator m_end;
};

/**
\brief  Convenience function that returns a sequence representation of the
        mapped values in a `std::map` or `std::unordered_map` (or any other
        type that has the same API).

This function allows for easy construction of a Sequence backed by a
MapValueSequenceImpl, for example:
~~~{.cpp}
std::map<int, double> m;
Sequence<double> s = MapValueSequence(m);
~~~
*/
template<typename Map>
Sequence<typename Map::mapped_type&> MapValueSequence(Map& map)
{
    return Sequence<typename Map::mapped_type&>(
        std::make_shared<MapValueSequenceImpl<Map>>(map));
}

namespace detail
{
    // Helper template that strips the & off a reference type.
    template<typename T> struct ValueType { typedef T Type; };
    template<typename T> struct ValueType<T&> { typedef T Type; };
}

/**
\brief  An implementation of an empty sequence, i.e. one for which Empty()
        is always `true`.
*/
template<typename ElementT>
class EmptySequenceImpl : public ISequenceImpl<ElementT>
{
public:
    bool Empty() DSB_FINAL override { return true; }

    ElementT Next() DSB_FINAL override
    {
        assert(!"Next() called on empty sequence");
        return *(static_cast<typename detail::ValueType<ElementT>::Type*>(nullptr));
    }
};

/// Returns an empty sequence, i.e. one for which Empty() is always `true`.
template<typename ElementT>
Sequence<ElementT> EmptySequence()
{
    return Sequence<ElementT>(std::make_shared<EmptySequenceImpl<ElementT>>());
}

}}      // namespace
#endif  // header guard
<commit_msg>Added ConstSequence()<commit_after>/**
\file
\brief Main header file for dsb::sequence.
*/
#ifndef DSB_SEQUENCE_HPP
#define DSB_SEQUENCE_HPP

#include <cassert>
#include <iterator>
#include <memory>

#include "dsb/config.h"

namespace dsb
{

/**
\brief  Module that defines a sequence iteration abstraction, similar to
        standard iterators, but simpler.

The central class in this module is dsb::sequence::Sequence.  Please see
its documentation for more information.
*/
namespace sequence
{

/**
\brief  An interface for sequence implementations.

To create a new sequence type (e.g. to support a new kind of backing storage),
create a class which inherits and implements this interface, and pass an
object of that class to the Sequence constructor.

`ElementT` is the type of the elements in the sequence, i.e., the return type
of Next().

\see Sequence
*/
template<typename ElementT>
class ISequenceImpl
{
public:
    /// Returns whether we have reached the end of the sequence.
    virtual bool Empty() = 0;

    /**
    \brief  Returns the element currently at the front of the sequence
            and iterates one step past it.

    It is OK to assume that Empty() has, or would have, returned `false`
    before this function is called (but good practice to verify it with
    an assertion).
    */
    virtual ElementT Next() = 0;

    // Allows deletions through a base class pointer.
    virtual ~ISequenceImpl() { }
};

/**
\brief  A class that is used to iterate over a sequence of elements of
        type `ElementT`.

This class defines two functions, Empty() and Next().  The former returns
whether we have reached the end of the sequence, and the latter returns
the element at the head of the range and simultaneously iterates past it.
The idiomatic way to iterate a sequence is thus:
~~~~{.cpp}
while (!sequence.Empty()) {
    auto element = seq.Next();
    // Do stuff with element here
}
~~~~

Sequence objects fulfil the main purpose of standard iterators, but
are different from them in several ways, some of which are advantages
and others of which are drawbacks, depending on the situation:

  - A Sequence currently only allows one-way iteration.  (In that respect
    it is similar to an [input iterator](http://www.cplusplus.com/reference/iterator/InputIterator/).)

  - A Sequence is actually more like a *pair* of iterators: the beginning
    and the end of the sequence.  This makes it easier to use, because it
    is only necessary to keep track of one object instead of two.  If `b`
    is the "begin" iterator and `e` is the "end" iterator, then Empty() is
    equivalent to `b==e`, while Next() is equivalent to `*b++`.

  - Iterators are typically value types, while Sequence has reference
    semantics.  That is, if `a` is a sequence, and we set `b = a`, then
    calling either `a.Next()` or `b.Next()` will iterate both sequences
    one step.

  - The type of a Sequence only depends on the value of its elements, and
    not on the underlying container.  That is, an object of type
    `Sequence<int>` may be used to iterate over the contents of a
    `std::vector<int>`, an `std::list<int>`, or even a plain array of
    `int`s.

  - A Sequence typically has worse performance than an iterator.  This is
    related to the previous point.  Polymorphic classes are used "under the
    hood" to hide the type of the underlying container, which means that
    calls to Empty() and Next() are indirect (virtual), and that memory for
    the underlying objects has to be dynamically allocated.

The Sequence class is just a thin wrapper around a `std::shared_ptr` to
an object of type ISequenceImpl, which contains the actual implementation
of the sequence.  (This is how the type of the underlying container gets
hidden.)  To create a new sequence type it is therefore necessary to
create a class that implements the ISequenceImpl interface.

Like iterators, a Sequence object may be invalidated by changes to the
underlying storage.  The circumstances under which this does or doesn't
happen are defined by the specific ISequenceImpl implementation used.

Furthermore, if `ElementT` is a reference type (e.g. `int&`), it means that
Next() returns a reference to the value in some underlying storage.  In that
case, the lifetime of the reference is defined by the specific sequence
implementation.
*/
template<typename ElementT>
class Sequence
{
public:
    /// Constructs a new sequence with the given implementation.
    Sequence(std::shared_ptr<ISequenceImpl<ElementT>> impl
                = std::shared_ptr<ISequenceImpl<ElementT>>())
        : m_impl(impl)
    { }

    typedef ElementT ElementType;

    /// Returns whether we have reached the end of the sequence.
    bool Empty() { return !m_impl.get() || m_impl->Empty(); }

    /**
    \brief  Returns the element currently at the front of the sequence
            and iterates one step past it.

    Calling this function is only allowed if Empty() has, or would have,
    returned `false`.
    */
    ElementT Next() { return m_impl->Next(); }

private:
    std::shared_ptr<ISequenceImpl<ElementT>> m_impl;
};

/**
\brief  A sequence implementation that wraps a pair of standard iterators.

`Iterator` is the iterator type.  The sequence is valid as long as the
iterators are valid (which again depends on the type of container iterated).

\see ArraySequence, ContainerSequence
*/
template<typename Iterator>
class IteratorSequenceImpl
    : public ISequenceImpl<typename std::iterator_traits<Iterator>::reference>
{
public:
    /**
    \brief  Constructor that takes the "begin" and "end" iterators of a
            sequence.

    The two iterators must be comparable.
    */
    IteratorSequenceImpl(Iterator begin, Iterator end)
        : m_begin(begin), m_end(end)
    { }

    bool Empty() DSB_FINAL override
    {
        return m_begin == m_end;
    }

    typename std::iterator_traits<Iterator>::reference Next() DSB_FINAL override
    {
        return *(m_begin++);
    }

private:
    Iterator m_begin;
    Iterator m_end;
};

/**
\brief  Convenience function which returns a Sequence that iterates over the
        entire contents of a standard container.

The sequence is backed by an IteratorSequenceImpl.
*/
template<typename Container>
Sequence<typename std::iterator_traits<typename Container::iterator>::reference>
    ContainerSequence(Container& c)
{
    typedef typename Container::iterator I;
    typedef typename std::iterator_traits<I>::reference E;
    return Sequence<E>(std::make_shared<IteratorSequenceImpl<I>>(
        c.begin(), c.end()));
}

// Same as the above, but for const containers.
template<typename Container>
Sequence<typename std::iterator_traits<typename Container::const_iterator>::reference>
    ContainerSequence(const Container& c)
{
    typedef typename Container::const_iterator I;
    typedef typename std::iterator_traits<I>::reference E;
    return Sequence<E>(std::make_shared<IteratorSequenceImpl<I>>(
        c.cbegin(), c.cend()));
}

/**
\brief  Convenience function which returns a Sequence that iterates over the
        entire contents of an array.

The sequence is backed by an IteratorSequenceImpl.
*/
template<typename ElementT>
Sequence<ElementT&> ArraySequence(ElementT* pointer, size_t length)
{
    return Sequence<ElementT&>(std::make_shared<IteratorSequenceImpl<ElementT*>>(
        pointer, pointer + length));
}

/**
\brief  A sequence implementation that allows iteration over the mapped values
        of a `std::map` or `std::unordered_map` (or any other type that has
        the same API).

The class stores and uses a pair of map iterators, so the sequence remains
valid under the same circumstances as the iterators.
*/
template<typename Map>
class MapValueSequenceImpl : public ISequenceImpl<typename Map::mapped_type&>
{
public:
    /// Constructor that takes a map object.
    MapValueSequenceImpl(Map& map)
        : m_begin(map.begin()), m_end(map.end())
    { }

    bool Empty() DSB_FINAL override
    {
        return m_begin == m_end;
    }

    typename Map::mapped_type& Next() DSB_FINAL override
    {
        return (m_begin++)->second;
    }

private:
    typename Map::iterator m_begin;
    typename Map::iterator m_end;
};

/**
\brief  Convenience function that returns a sequence representation of the
        mapped values in a `std::map` or `std::unordered_map` (or any other
        type that has the same API).

This function allows for easy construction of a Sequence backed by a
MapValueSequenceImpl, for example:
~~~{.cpp}
std::map<int, double> m;
Sequence<double> s = MapValueSequence(m);
~~~
*/
template<typename Map>
Sequence<typename Map::mapped_type&> MapValueSequence(Map& map)
{
    return Sequence<typename Map::mapped_type&>(
        std::make_shared<MapValueSequenceImpl<Map>>(map));
}

namespace detail
{
    // Helper template that strips the & off a reference type.
    template<typename T> struct ValueType { typedef T Type; };
    template<typename T> struct ValueType<T&> { typedef T Type; };
}

/**
\brief  An implementation of an empty sequence, i.e. one for which Empty()
        is always `true`.
*/
template<typename ElementT>
class EmptySequenceImpl : public ISequenceImpl<ElementT>
{
public:
    bool Empty() DSB_FINAL override { return true; }

    ElementT Next() DSB_FINAL override
    {
        assert(!"Next() called on empty sequence");
        return *(static_cast<typename detail::ValueType<ElementT>::Type*>(nullptr));
    }
};

/// Returns an empty sequence, i.e. one for which Empty() is always `true`.
template<typename ElementT>
Sequence<ElementT> EmptySequence()
{
    return Sequence<ElementT>(std::make_shared<EmptySequenceImpl<ElementT>>());
}

// A read-only view of another sequence; see ConstSequence().
template<typename DerefElementT>
class ConstSequenceImpl : public ISequenceImpl<const DerefElementT&>
{
public:
    ConstSequenceImpl(Sequence<DerefElementT&> wrapThis) : m_wrapped(wrapThis) { }
    bool Empty() DSB_FINAL override { return m_wrapped.Empty(); }
    const DerefElementT& Next() DSB_FINAL override { return m_wrapped.Next(); }
private:
    Sequence<DerefElementT&> m_wrapped;
};

/**
\brief  Returns a sequence which provides a read-only view of the elements in
        another sequence.
*/
template<typename DerefElementT>
Sequence<const DerefElementT&> ConstSequence(Sequence<DerefElementT&> sequence)
{
    return Sequence<const DerefElementT&>(
        std::make_shared<ConstSequenceImpl<DerefElementT>>(sequence));
}

}}      // namespace
#endif  // header guard
<|endoftext|>