text:
#include <iksemel.h>
#include <pcre.h>
#include <stdbool.h>
#include <string.h>
#include <sstream>
#include <map>

#include "cspeech.h"
#include "srgs.h"

#define MAX_RECURSION 100
#define MAX_TAGS 30

/** function to handle tag attributes */
typedef int (* tag_attribs_fn)(struct srgs_grammar *, char **);
/** function to handle tag CDATA */
typedef int (* tag_cdata_fn)(struct srgs_grammar *, char *, size_t);

/**
 * Tag definition
 */
struct tag_def {
  tag_attribs_fn attribs_fn;
  tag_cdata_fn cdata_fn;
  bool is_root;
  std::map<const char *,const char *> children_tags;
};

/**
 * library configuration
 */
static struct {
  /** true if initialized */
  bool init;
  /** Mapping of tag name to definition */
  std::map<const char *,struct tag_def *> tag_defs;
  /** library memory pool */
  switch_memory_pool_t *pool;
  /** Callback for logging messages **/
  int (*logging_callback)(void *context, cspeech_log_level_t log_level, const char *log_message, ...);
} globals;

/**
 * SRGS node types
 */
enum srgs_node_type {
  /** anything */
  SNT_ANY,
  /** <grammar> */
  SNT_GRAMMAR,
  /** <rule> */
  SNT_RULE,
  /** <one-of> */
  SNT_ONE_OF,
  /** <item> */
  SNT_ITEM,
  /** <ruleref> unresolved reference to node */
  SNT_UNRESOLVED_REF,
  /** <ruleref> resolved reference to node */
  SNT_REF,
  /** <item> string */
  SNT_STRING,
  /** <tag> */
  SNT_TAG,
  /** <lexicon> */
  SNT_LEXICON,
  /** <example> */
  SNT_EXAMPLE,
  /** <token> */
  SNT_TOKEN,
  /** <meta> */
  SNT_META,
  /** <metadata> */
  SNT_METADATA
};

/**
 * <rule> value
 */
struct rule_value {
  char is_public;
  const char *id;
  char *regex;
};

/**
 * <item> value
 */
struct item_value {
  int repeat_min;
  int repeat_max;
  const char *weight;
  int tag;
};

/**
 * <ruleref> value
 */
union ref_value {
  struct srgs_node *node;
  char *uri;
};

/**
 * A node in the SRGS parse tree
 */
struct srgs_node {
  /** Name of node */
  const char *name;
  /** Type of node */
  enum srgs_node_type type;
  /** True if node has been inspected for loops */
  char visited;
  /** Node value */
  union {
    char *root;
    const char *string;
    union ref_value ref;
    struct rule_value rule;
    struct item_value item;
  } value;
  /** parent node */
  struct srgs_node *parent;
  /** child node */
  struct srgs_node *child;
  /** sibling node */
  struct srgs_node *next;
  /** number of child nodes */
  int num_children;
  /** tag handling data */
  struct tag_def *tag_def;
};

/**
 * A parsed grammar
 */
struct srgs_grammar {
  /** grammar memory pool */
  switch_memory_pool_t *pool;
  /** current node being parsed */
  struct srgs_node *cur;
  /** rule names mapped to node */
  std::map<const char *,struct srgs_node *> rules;
  /** possible matching tags */
  const char *tags[MAX_TAGS + 1];
  /** number of tags */
  int tag_count;
  /** grammar encoding */
  char *encoding;
  /** grammar language */
  char *language;
  /** true if digit grammar */
  int digit_mode;
  /** grammar parse tree root */
  struct srgs_node *root;
  /** root rule */
  struct srgs_node *root_rule;
  /** compiled grammar regex */
  pcre *compiled_regex;
  /** grammar in regex format */
  char *regex;
  /** grammar in JSGF format */
  char *jsgf;
  /** grammar as JSGF file */
  char *jsgf_file_name;
  /** synchronizes access to this grammar */
  switch_mutex_t *mutex;
  /** optional uuid for logging */
  const char *uuid;
};

/**
 * The SRGS SAX parser
 */
struct srgs_parser {
  /** parser memory pool */
  switch_memory_pool_t *pool;
  /** grammar cache */
  switch_hash_t *cache;
  /** cache mutex */
  switch_mutex_t *mutex;
  /** optional uuid for logging */
  const char *uuid;
};

/**
 * Convert entity name to node type
 * @param name of entity
 * @return the type or ANY
 */
static enum srgs_node_type string_to_node_type(char *name)
{
  if (!strcmp("grammar", name)) {
    return SNT_GRAMMAR;
  }
  if (!strcmp("item", name)) {
    return SNT_ITEM;
  }
  if (!strcmp("one-of", name)) {
    return SNT_ONE_OF;
  }
  if (!strcmp("ruleref", name)) {
    return SNT_UNRESOLVED_REF;
  }
  if (!strcmp("rule", name)) {
    return SNT_RULE;
  }
  if (!strcmp("tag", name)) {
    return SNT_TAG;
  }
  if (!strcmp("lexicon", name)) {
    return SNT_LEXICON;
  }
  if (!strcmp("example", name)) {
    return SNT_EXAMPLE;
  }
  if (!strcmp("token", name)) {
    return SNT_TOKEN;
  }
  if (!strcmp("meta", name)) {
    return SNT_META;
  }
  if (!strcmp("metadata", name)) {
    return SNT_METADATA;
  }
  return SNT_ANY;
}

/**
 * Log node
 */
static void sn_log_node_open(struct srgs_node *node)
{
  switch (node->type) {
    case SNT_ANY:
    case SNT_METADATA:
    case SNT_META:
    case SNT_TOKEN:
    case SNT_EXAMPLE:
    case SNT_LEXICON:
    case SNT_TAG:
    case SNT_ONE_OF:
    case SNT_GRAMMAR:
      if(globals.logging_callback) {
        globals.logging_callback(NULL, CSPEECH_LOG_DEBUG, "<%s>\n", node->name);
      }
      return;
    case SNT_RULE:
      if(globals.logging_callback) {
        globals.logging_callback(NULL, CSPEECH_LOG_DEBUG, "<rule id='%s' scope='%s'>\n", node->value.rule.id, node->value.rule.is_public ? "public" : "private");
      }
      return;
    case SNT_ITEM:
      if(globals.logging_callback) {
        globals.logging_callback(NULL, CSPEECH_LOG_DEBUG, "<item repeat='%i'>\n", node->value.item.repeat_min);
      }
      return;
    case SNT_UNRESOLVED_REF:
      if(globals.logging_callback) {
        globals.logging_callback(NULL, CSPEECH_LOG_DEBUG, "<ruleref (unresolved) uri='%s'\n", node->value.ref.uri);
      }
      return;
    case SNT_REF:
      if(globals.logging_callback) {
        globals.logging_callback(NULL, CSPEECH_LOG_DEBUG, "<ruleref uri='#%s'>\n", node->value.ref.node->value.rule.id);
      }
      return;
    case SNT_STRING:
      if(globals.logging_callback) {
        globals.logging_callback(NULL, CSPEECH_LOG_DEBUG, "%s\n", node->value.string);
      }
      return;
  }
}

/**
 * Log node
 */
static void sn_log_node_close(struct srgs_node *node)
{
  switch (node->type) {
    case SNT_GRAMMAR:
    case SNT_RULE:
    case SNT_ONE_OF:
    case SNT_ITEM:
    case SNT_REF:
    case SNT_TAG:
    case SNT_LEXICON:
    case SNT_EXAMPLE:
    case SNT_TOKEN:
    case SNT_META:
    case SNT_METADATA:
    case SNT_ANY:
      if(globals.logging_callback) {
        globals.logging_callback(NULL, CSPEECH_LOG_DEBUG, "</%s>\n", node->name);
      }
      return;
    case SNT_UNRESOLVED_REF:
      if(globals.logging_callback) {
        globals.logging_callback(NULL, CSPEECH_LOG_DEBUG, "</ruleref (unresolved)>\n");
      }
      return;
    case SNT_STRING:
      return;
  }
}

/**
 * Create a new node
 * @param name of node
 * @param type of node
 * @return the node
 */
static struct srgs_node *sn_new(const char *name, enum srgs_node_type type)
{
  struct srgs_node *node = (struct srgs_node *) malloc(sizeof(srgs_node));
  node->name = strdup(name);
  node->type = type;
  return node;
}

/**
 * @param node to search
 * @return the last sibling of node
 */
static struct srgs_node *sn_find_last_sibling(struct srgs_node *node)
{
  if (node && node->next) {
    return sn_find_last_sibling(node->next);
  }
  return node;
}

/**
 * Add child node
 * @param pool to use
 * @param parent node to add child to
 * @param name the child node name
 * @param type the child node type
 * @return the child node
 */
static struct srgs_node *sn_insert(switch_memory_pool_t *pool, struct srgs_node *parent, const char *name, enum srgs_node_type type)
{
  struct srgs_node *sibling = parent ? sn_find_last_sibling(parent->child) : NULL;
  struct srgs_node *child = sn_new(name, type);
  if (parent) {
    parent->num_children++;
    child->parent = parent;
  }
  if (sibling) {
    sibling->next = child;
  } else if (parent) {
    parent->child = child;
  }
  return child;
}

/**
 * Add string child node
 * @param pool to use
 * @param parent node to add string to
 * @param string to add - this function does not copy the string
 * @return the string child node
 */
static struct srgs_node *sn_insert_string(switch_memory_pool_t *pool, struct srgs_node *parent, char *string)
{
  struct srgs_node *child = sn_insert(pool, parent, string, SNT_STRING);
  child->value.string = string;
  return child;
}

/**
 * Add a definition for a tag
 * @param tag the name
 * @param attribs_fn the function to handle the tag attributes
 * @param cdata_fn the function to handler the tag CDATA
 * @param children_tags comma-separated list of valid child tag names
 * @return the definition
 */
static struct tag_def *add_tag_def(const char *tag, tag_attribs_fn attribs_fn, tag_cdata_fn cdata_fn, const char *children_tags)
{
  struct tag_def *def = switch_core_alloc(globals.pool, sizeof(*def));
  if (!cspeech_zstr(children_tags)) {
    std::string tags_string(children_tags);
    std::stringstream ss(tags_string);
    std::string item;
    while (std::getline(ss, item, ',')) {
      def->children_tags[item.c_str()] = item.c_str();
    }
  }
  def->attribs_fn = attribs_fn;
  def->cdata_fn = cdata_fn;
  def->is_root = false;
  globals.tag_defs[tag] = def;
  return def;
}

/**
 * Add a definition for a root tag
 * @param tag the name
 * @param attribs_fn the function to handle the tag attributes
 * @param cdata_fn the function to handler the tag CDATA
 * @param children_tags comma-separated list of valid child tag names
 * @return the definition
 */
static struct tag_def *add_root_tag_def(const char *tag, tag_attribs_fn attribs_fn, tag_cdata_fn cdata_fn, const char *children_tags)
{
  struct tag_def *def = add_tag_def(tag, attribs_fn, cdata_fn, children_tags);
  def->is_root = true;
  return def;
}

/**
 * Handle tag attributes
 * @param parser the parser
 * @param name the tag name
 * @param atts the attributes
 * @return IKS_OK if OK IKS_BADXML on parse failure
 */
static int process_tag(struct srgs_grammar *grammar, const char *name, char **atts)
{
  struct srgs_node *cur = grammar->cur;
  if (cur->tag_def->is_root && cur->parent == NULL) {
    /* no parent for ROOT tags */
    return cur->tag_def->attribs_fn(grammar, atts);
  } else if (!cur->tag_def->is_root && cur->parent) {
    /* check if this child is allowed by parent node */
    struct tag_def *parent_def = cur->parent->tag_def;
    if (parent_def->children_tags.count("ANY") > 0 ||
      parent_def->children_tags.count(name) > 0) {
      return cur->tag_def->attribs_fn(grammar, atts);
    } else {
      if(globals.logging_callback) {
        globals.logging_callback(grammar, CSPEECH_LOG_INFO, "<%s> cannot be a child of <%s>\n", name, cur->parent->name);
      }
    }
  } else if (cur->tag_def->is_root && cur->parent != NULL) {
    if(globals.logging_callback) {
      globals.logging_callback(grammar, CSPEECH_LOG_INFO, "<%s> must be the root element\n", name);
    }
  } else {
    if(globals.logging_callback) {
      globals.logging_callback(grammar, CSPEECH_LOG_INFO, "<%s> cannot be a root element\n", name);
    }
  }
  return IKS_BADXML;
}

/**
 * Handle tag attributes that are ignored
 * @param grammar the grammar
 * @param atts the attributes
 * @return IKS_OK
 */
static int process_attribs_ignore(struct srgs_grammar *grammar, char **atts)
{
  return IKS_OK;
}

/**
 * Handle CDATA that is ignored
 * @param grammar the grammar
 * @param data the CDATA
 * @param len the CDATA length
 * @return IKS_OK
 */
static int process_cdata_ignore(struct srgs_grammar *grammar, char *data, size_t len)
{
  return IKS_OK;
}

/**
 * Handle CDATA that is not allowed
 * @param grammar the grammar
 * @param data the CDATA
 * @param len the CDATA length
 * @return IKS_BADXML if any printable characters
 */
static int process_cdata_bad(struct srgs_grammar *grammar, char *data, size_t len)
{
  int i;
  for (i = 0; i < len; i++) {
    if (isgraph(data[i])) {
      if(globals.logging_callback) {
        globals.logging_callback(grammar, CSPEECH_LOG_INFO, "Unexpected CDATA for <%s>\n", grammar->cur->name);
      }
      return IKS_BADXML;
    }
  }
  return IKS_OK;
}

/**
 * Process <rule> attributes
 * @param grammar the grammar state
 * @param atts the attributes
 * @return IKS_OK if ok
 */
static int process_rule(struct srgs_grammar *grammar, char **atts)
{
  struct srgs_node *rule = grammar->cur;
  rule->value.rule.is_public = 0;
  rule->value.rule.id = NULL;
  if (atts) {
    int i = 0;
    while (atts[i]) {
      if (!strcmp("scope", atts[i])) {
        rule->value.rule.is_public = !cspeech_zstr(atts[i + 1]) && !strcmp("public", atts[i + 1]);
      } else if (!strcmp("id", atts[i])) {
        if (!cspeech_zstr(atts[i + 1])) {
          rule->value.rule.id = strdup(atts[i + 1]);
        }
      }
      i += 2;
    }
  }

  if (cspeech_zstr(rule->value.rule.id)) {
    if(globals.logging_callback) {
      globals.logging_callback(grammar, CSPEECH_LOG_INFO, "Missing rule ID: %s\n", rule->value.rule.id);
    }
    return IKS_BADXML;
  }

  if (grammar->rules.count(rule->value.rule.id) > 0) {
    if(globals.logging_callback) {
      globals.logging_callback(grammar, CSPEECH_LOG_INFO, "Duplicate rule ID: %s\n", rule->value.rule.id);
    }
    return IKS_BADXML;
  }
  grammar->rules[rule->value.rule.id] = rule;

  return IKS_OK;
}

/**
 * Process <ruleref> attributes
 * @param grammar the grammar state
 * @param atts the attributes
 * @return IKS_OK if ok
 */
static int process_ruleref(struct srgs_grammar *grammar, char **atts)
{
  struct srgs_node *ruleref = grammar->cur;
  if (atts) {
    int i = 0;
    while (atts[i]) {
      if (!strcmp("uri", atts[i])) {
        char *uri = atts[i + 1];
        if (cspeech_zstr(uri)) {
          if(globals.logging_callback) {
            globals.logging_callback(grammar, CSPEECH_LOG_INFO, "Empty <ruleref> uri\n");
          }
          return IKS_BADXML;
        }
        /* only allow local reference */
        if (uri[0] != '#' || strlen(uri) < 2) {
          if(globals.logging_callback) {
            globals.logging_callback(grammar, CSPEECH_LOG_INFO, "Only local rule refs allowed\n");
          }
          return IKS_BADXML;
        }
        ruleref->value.ref.uri = strdup(uri);
        return IKS_OK;
      }
      i += 2;
    }
  }
  return IKS_OK;
}

/**
 * Process <item> attributes
 * @param grammar the grammar state
 * @param atts the attributes
 * @return IKS_OK if ok
 */
static int process_item(struct srgs_grammar *grammar, char **atts)
{
  struct srgs_node *item = grammar->cur;
  item->value.item.repeat_min = 1;
  item->value.item.repeat_max = 1;
  item->value.item.weight = NULL;
  if (atts) {
    int i = 0;
    while (atts[i]) {
      if (!strcmp("repeat", atts[i])) {
        /* repeats of 0 are not supported by this code */
        char *repeat = atts[i + 1];
        if (cspeech_zstr(repeat)) {
          if(globals.logging_callback) {
            globals.logging_callback(grammar, CSPEECH_LOG_INFO, "Empty <item> repeat atribute\n");
          }
          return IKS_BADXML;
        }
        if (cspeech_is_number(repeat)) {
          /* single number */
          int repeat_val = atoi(repeat);
          if (repeat_val < 1) {
            if(globals.logging_callback) {
              globals.logging_callback(grammar, CSPEECH_LOG_INFO, "<item> repeat must be >= 0\n");
            }
            return IKS_BADXML;
          }
          item->value.item.repeat_min = repeat_val;
          item->value.item.repeat_max = repeat_val;
        } else {
          /* range */
          char *min = strdup(repeat);
          char *max = strchr(min, '-');
          if (max) {
            *max = '\0';
            max++;
          } else {
            if(globals.logging_callback) {
              globals.logging_callback(grammar, CSPEECH_LOG_INFO, "<item> repeat must be a number or range\n");
            }
            return IKS_BADXML;
          }
          if (cspeech_is_number(min) && (cspeech_is_number(max) || cspeech_zstr(max))) {
            int min_val = atoi(min);
            int max_val = cspeech_zstr(max) ? INT_MAX : atoi(max);
            /* max must be >= min and > 0
               min must be >= 0 */
            if ((max_val <= 0) || (max_val < min_val) || (min_val < 0)) {
              if(globals.logging_callback) {
                globals.logging_callback(grammar, CSPEECH_LOG_INFO, "<item> repeat range invalid\n");
              }
              return IKS_BADXML;
            }
            item->value.item.repeat_min = min_val;
            item->value.item.repeat_max = max_val;
          } else {
            if(globals.logging_callback) {
              globals.logging_callback(grammar, CSPEECH_LOG_INFO, "<item> repeat range is not a number\n");
            }
            return IKS_BADXML;
          }
        }
      } else if (!strcmp("weight", atts[i])) {
        const char *weight = atts[i + 1];
        if (cspeech_zstr(weight) || !cspeech_is_number(weight) || atof(weight) < 0) {
          if(globals.logging_callback) {
            globals.logging_callback(grammar, CSPEECH_LOG_INFO, "<item> weight is not a number >= 0\n");
          }
          return IKS_BADXML;
        }
        item->value.item.weight = strdup(weight);
      }
      i += 2;
    }
  }
  return IKS_OK;
}

/**
 * Process <grammar> attributes
 * @param grammar the grammar state
 * @param atts the attributes
 * @return IKS_OK if ok
 */
static int process_grammar(struct srgs_grammar *grammar, char **atts)
{
  if (grammar->root) {
    if(globals.logging_callback) {
      globals.logging_callback(grammar, CSPEECH_LOG_INFO, "Only one <grammar> tag allowed\n");
    }
    return IKS_BADXML;
  }
  grammar->root = grammar->cur;
  if (atts) {
    int i = 0;
    while (atts[i]) {
      if (!strcmp("mode", atts[i])) {
        char *mode = atts[i + 1];
        if (cspeech_zstr(mode)) {
          if(globals.logging_callback) {
            globals.logging_callback(grammar, CSPEECH_LOG_INFO, "<grammar> mode is missing\n");
          }
          return IKS_BADXML;
        }
        grammar->digit_mode = !strcasecmp(mode, "dtmf");
      } else if(!strcmp("encoding", atts[i])) {
        char *encoding = atts[i + 1];
        if (cspeech_zstr(encoding)) {
          if(globals.logging_callback) {
            globals.logging_callback(grammar, CSPEECH_LOG_INFO, "<grammar> encoding is empty\n");
          }
          return IKS_BADXML;
        }
        grammar->encoding = strdup(encoding);
      } else if (!strcmp("language", atts[i])) {
        char *language = atts[i + 1];
        if (cspeech_zstr(language)) {
          if(globals.logging_callback) {
            globals.logging_callback(grammar, CSPEECH_LOG_INFO, "<grammar> language is empty\n");
          }
          return IKS_BADXML;
        }
        grammar->language = strdup(language);
      } else if (!strcmp("root", atts[i])) {
        char *root = atts[i + 1];
        if (cspeech_zstr(root)) {
          if(globals.logging_callback) {
            globals.logging_callback(grammar, CSPEECH_LOG_INFO, "<grammar> root is empty\n");
          }
          return IKS_BADXML;
        }
        grammar->cur->value.root = strdup(root);
      }
      i += 2;
    }
  }
  return IKS_OK;
}

/**
 * Process a tag
 */
static int tag_hook(void *user_data, char *name, char **atts, int type)
{
  int result = IKS_OK;
  struct srgs_grammar *grammar = (struct srgs_grammar *)user_data;

  if (type == IKS_OPEN || type == IKS_SINGLE) {
    enum srgs_node_type ntype = string_to_node_type(name);
    grammar->cur = sn_insert(grammar->cur, name, ntype);
    grammar->cur->tag_def = globals.tag_defs[name];
    if (!grammar->cur->tag_def) {
      grammar->cur->tag_def = globals.tag_defs["ANY"];
    }
    result = process_tag(grammar, name, atts);
    sn_log_node_open(grammar->cur);
  }

  if (type == IKS_CLOSE || type == IKS_SINGLE) {
    sn_log_node_close(grammar->cur);
    grammar->cur = grammar->cur->parent;
  }

  return result;
}

/**
 * Process <tag> CDATA
 * @param grammar the grammar
 * @param data the CDATA
 * @param len the CDATA length
 * @return IKS_OK
 */
static int process_cdata_tag(struct srgs_grammar *grammar, char *data, size_t len)
{
  struct srgs_node *item = grammar->cur->parent;
  if (item && item->type == SNT_ITEM) {
    if (grammar->tag_count < MAX_TAGS) {
      /* grammar gets the tag name, item gets the unique tag number */
      char *tag = (char *)malloc(sizeof(char) * (len + 1));
      tag[len] = '\0';
      strncpy(tag, data, len);
      grammar->tags[++grammar->tag_count] = tag;
      item->value.item.tag = grammar->tag_count;
    } else {
      if(globals.logging_callback) {
        globals.logging_callback(NULL, CSPEECH_LOG_WARNING, "too many <tag>s\n");
      }
      return IKS_BADXML;
    }
  }
  return IKS_OK;
}

/**
 * Process CDATA grammar tokens
 * @param grammar the grammar
 * @param data the CDATA
 * @param len the CDATA length
 * @return IKS_OK
 */
static int process_cdata_tokens(struct srgs_grammar *grammar, char *data, size_t len)
{
  struct srgs_node *string = grammar->cur;
  int i;
  if (grammar->digit_mode) {
    for (i = 0; i < len; i++) {
      if (isdigit(data[i]) || data[i] == '#' || data[i] == '*') {
        char *digit = (char *)malloc(sizeof(char) * 2);
        digit[0] = data[i];
        digit[1] = '\0';
        string = sn_insert_string(string, digit);
        sn_log_node_open(string);
      }
    }
  } else {
    char *data_dup = (char *)malloc(sizeof(char) * (len + 1));
    char *start = data_dup;
    char *end = start + len - 1;
    memcpy(data_dup, data, len);
    /* remove start whitespace */
    for (; start && *start && !isgraph(*start); start++) {
    }
    if (!cspeech_zstr(start)) {
      /* remove end whitespace */
      for (; end != start && *end && !isgraph(*end); end--) {
        *end = '\0';
      }
      if (!cspeech_zstr(start)) {
        string = sn_insert_string(string, start);
      }
    }
  }
  return IKS_OK;
}

/**
 * Process cdata
 * @param user_data the grammar
 * @param data the CDATA
 * @param len the CDATA length
 * @return IKS_OK
 */
static int cdata_hook(void *user_data, char *data, size_t len)
{
  struct srgs_grammar *grammar = (struct srgs_grammar *)user_data;
  if (!grammar) {
    if(globals.logging_callback) {
      globals.logging_callback(NULL, CSPEECH_LOG_INFO, "Missing grammar\n");
    }
    return IKS_BADXML;
  }
  if (grammar->cur) {
    if (grammar->cur->tag_def) {
      return grammar->cur->tag_def->cdata_fn(grammar, data, len);
    }
    if(globals.logging_callback) {
      globals.logging_callback(grammar, CSPEECH_LOG_INFO, "Missing definition for <%s>\n", grammar->cur->name);
    }
    return IKS_BADXML;
  }
  return IKS_OK;
}

/**
 * Create a new parsed grammar
 * @param parser
 * @return the grammar
 */
struct srgs_grammar *srgs_grammar_new(struct srgs_parser *parser)
{
  switch_memory_pool_t *pool = NULL;
  struct srgs_grammar *grammar = NULL;
  switch_core_new_memory_pool(&pool);
  grammar = switch_core_alloc(pool, sizeof (*grammar));
  grammar->root = NULL;
  grammar->cur = NULL;
  grammar->uuid = (parser && !cspeech_zstr(parser->uuid)) ? switch_core_strdup(pool, parser->uuid) : "";
  switch_mutex_init(&grammar->mutex, SWITCH_MUTEX_NESTED, pool);
  return grammar;
}

/**
 * Destroy a parsed grammar
 * @param grammar the grammar
 */
static void srgs_grammar_destroy(struct srgs_grammar *grammar)
{
  if (grammar->compiled_regex) {
    pcre_free(grammar->compiled_regex);
  }
  if (grammar->jsgf_file_name) {
    switch_file_remove(grammar->jsgf_file_name, pool);
  }
}

/**
 * Create a new parser.
 * @param uuid optional uuid for logging
 * @return the created parser
 */
struct srgs_parser *srgs_parser_new(const char *uuid)
{
  switch_memory_pool_t *pool = NULL;
  struct srgs_parser *parser = NULL;
  switch_core_new_memory_pool(&pool);
  if (pool) {
    parser = switch_core_alloc(pool, sizeof(*parser));
    parser->pool = pool;
    parser->uuid = cspeech_zstr(uuid) ? "" : switch_core_strdup(pool, uuid);
    switch_core_hash_init(&parser->cache, pool);
    switch_mutex_init(&parser->mutex, SWITCH_MUTEX_NESTED, pool);
  }
  return parser;
}

/**
 * Destroy the parser.
 * @param parser to destroy
 */
void srgs_parser_destroy(struct srgs_parser *parser)
{
  switch_memory_pool_t *pool = parser->pool;
  switch_hash_index_t *hi = NULL;

  /* clean up all cached grammars */
  for (hi = switch_core_hash_first(parser->cache); hi; hi = switch_core_hash_next(hi)) {
    struct srgs_grammar *grammar = NULL;
    const void *key;
    void *val;
    switch_core_hash_this(hi, &key, NULL, &val);
    grammar = (struct srgs_grammar *)val;
    switch_assert(grammar);
    srgs_grammar_destroy(grammar);
  }
  switch_core_destroy_memory_pool(&pool);
}

/**
 * Create regexes
 * @param grammar the grammar
 * @param node root node
 * @param stream set to NULL
 * @return 1 if successful
 */
static int create_regexes(struct srgs_grammar *grammar, struct srgs_node *node, switch_stream_handle_t *stream)
{
  sn_log_node_open(node);
  switch (node->type) {
    case SNT_GRAMMAR:
      if (node->child) {
        int num_rules = 0;
        struct srgs_node *child = node->child;
        if (grammar->root_rule) {
          if (!create_regexes(grammar, grammar->root_rule, NULL)) {
            return 0;
          }
          grammar->regex = switch_core_sprintf(grammar->pool, "^%s$", grammar->root_rule->value.rule.regex);
        } else {
          switch_stream_handle_t new_stream = { 0 };
          SWITCH_STANDARD_STREAM(new_stream);
          if (node->num_children > 1) {
            new_stream.write_function(&new_stream, "%s", "^(?:");
          } else {
            new_stream.write_function(&new_stream, "%s", "^");
          }
          for (; child; child = child->next) {
            if (!create_regexes(grammar, child, &new_stream)) {
              switch_safe_free(new_stream.data);
              return 0;
            }
            if (child->type == SNT_RULE && child->value.rule.is_public) {
              if (num_rules > 0) {
                new_stream.write_function(&new_stream, "%s", "|");
              }
              new_stream.write_function(&new_stream, "%s", child->value.rule.regex);
              num_rules++;
            }
          }
          if (node->num_children > 1) {
            new_stream.write_function(&new_stream, "%s", ")$");
          } else {
            new_stream.write_function(&new_stream, "%s", "$");
          }
          grammar->regex = strdup(new_stream.data);
          switch_safe_free(new_stream.data);
        }
        if(globals.logging_callback) {
          globals.logging_callback(grammar, CSPEECH_LOG_DEBUG, "document regex = %s\n", grammar->regex);
        }
      }
      break;
    case SNT_RULE:
      if (node->value.rule.regex) {
        return 1;
      } else if (node->child) {
        struct srgs_node *item = node->child;
        switch_stream_handle_t new_stream = { 0 };
        SWITCH_STANDARD_STREAM(new_stream);
        for (; item; item = item->next) {
          if (!create_regexes(grammar, item, &new_stream)) {
            if(globals.logging_callback) {
              globals.logging_callback(grammar, CSPEECH_LOG_DEBUG, "%s regex failed = %s\n", node->value.rule.id, node->value.rule.regex);
            }
            switch_safe_free(new_stream.data);
            return 0;
          }
        }
        node->value.rule.regex = strdup(new_stream.data);
        if(globals.logging_callback) {
          globals.logging_callback(grammar, CSPEECH_LOG_DEBUG, "%s regex = %s\n", node->value.rule.id, node->value.rule.regex);
        }
        switch_safe_free(new_stream.data);
      }
      break;
    case SNT_STRING: {
      int i;
      for (i = 0; i < strlen(node->value.string); i++) {
        switch (node->value.string[i]) {
          case '[':
          case '\\':
          case '^':
          case '$':
          case '.':
          case '|':
          case '?':
          case '*':
          case '+':
          case '(':
          case ')':
            /* escape special PCRE regex characters */
            stream->write_function(stream, "\\%c", node->value.string[i]);
            break;
          default:
            stream->write_function(stream, "%c", node->value.string[i]);
            break;
        }
      }
      if (node->child) {
        if (!create_regexes(grammar, node->child, stream)) {
          return 0;
        }
      }
      break;
    }
    case SNT_ITEM:
      if (node->child) {
        struct srgs_node *item = node->child;
        if (node->value.item.repeat_min != 1 || node->value.item.repeat_max != 1 || node->value.item.tag) {
          if (node->value.item.tag) {
            stream->write_function(stream, "(?P<%d>", node->value.item.tag);
          } else {
            stream->write_function(stream, "%s", "(?:");
          }
        }
        for(; item; item = item->next) {
          if (!create_regexes(grammar, item, stream)) {
            return 0;
          }
        }
        if (node->value.item.repeat_min != 1 || node->value.item.repeat_max != 1) {
          if (node->value.item.repeat_min != node->value.item.repeat_max) {
            if (node->value.item.repeat_min == 0 && node->value.item.repeat_max == INT_MAX) {
                stream->write_function(stream, ")*");
            } else if (node->value.item.repeat_min == 0 && node->value.item.repeat_max == 1) {
                stream->write_function(stream, ")?");
            } else if (node->value.item.repeat_min == 1 && node->value.item.repeat_max == INT_MAX) {
              stream->write_function(stream, ")+");
            } else if (node->value.item.repeat_max == INT_MAX) {
              stream->write_function(stream, "){%i,1000}", node->value.item.repeat_min);
            } else {
              stream->write_function(stream, "){%i,%i}", node->value.item.repeat_min, node->value.item.repeat_max);
            }
          } else {
            stream->write_function(stream, "){%i}", node->value.item.repeat_min);
          }
        } else if (node->value.item.tag) {
          stream->write_function(stream, "%s", ")");
        }
      }
      break;
    case SNT_ONE_OF:
      if (node->child) {
        struct srgs_node *item = node->child;
        if (node->num_children > 1) {
          stream->write_function(stream, "%s", "(?:");
        }
        for (; item; item = item->next) {
          if (item != node->child) {
            stream->write_function(stream, "%s", "|");
          }
          if (!create_regexes(grammar, item, stream)) {
            return 0;
          }
        }
        if (node->num_children > 1) {
          stream->write_function(stream, "%s", ")");
        }
      }
      break;
    case SNT_REF: {
      struct srgs_node *rule = node->value.ref.node;
      if (!rule->value.rule.regex) {
        if(globals.logging_callback) {
          globals.logging_callback(grammar, CSPEECH_LOG_DEBUG, "ruleref: create %s regex\n", rule->value.rule.id);
        }
        if (!create_regexes(grammar, rule, NULL)) {
          return 0;
        }
      }
      if (!rule->value.rule.regex) {
        return 0;
      }
      stream->write_function(stream, "%s", rule->value.rule.regex);
      break;
    }
    case SNT_ANY:
    default:
      /* ignore */
      return 1;
  }
  sn_log_node_close(node);
  return 1;
}

/**
 * Compile regex
 */
static pcre *get_compiled_regex(struct srgs_grammar *grammar)
{
  int erroffset = 0;
  const char *errptr = "";
  int options = 0;
  const char *regex;

  if (!grammar) {
    if(globals.logging_callback) {
      globals.logging_callback(grammar, CSPEECH_LOG_CRIT, "grammar is NULL!\n");
    }
    return NULL;
  }

  switch_mutex_lock(grammar->mutex);
  if (!grammar->compiled_regex && (regex = srgs_grammar_to_regex(grammar))) {
    if (!(grammar->compiled_regex = pcre_compile(regex, options, &errptr, &erroffset, NULL))) {
      if(globals.logging_callback) {
        globals.logging_callback(grammar, CSPEECH_LOG_WARNING, "Failed to compile grammar regex: %s\n", regex);
      }
    }
  }
  switch_mutex_unlock(grammar->mutex);
  return grammar->compiled_regex;
}

/**
 * Resolve all unresolved references and detect loops.
 * @param grammar the grammar
 * @param node the current node
 * @param level the recursion level
 */
static int resolve_refs(struct srgs_grammar *grammar, struct srgs_node *node, int level)
{
  sn_log_node_open(node);
  if (node->visited) {
    if(globals.logging_callback) {
      globals.logging_callback(grammar, CSPEECH_LOG_INFO, "Loop detected.\n");
    }
    return 0;
  }
  node->visited = 1;

  if (level > MAX_RECURSION) {
    if(globals.logging_callback) {
      globals.logging_callback(grammar, CSPEECH_LOG_INFO, "Recursion too deep.\n");
    }
    return 0;
  }

  if (node->type == SNT_GRAMMAR && node->value.root) {
    struct srgs_node *rule = (struct srgs_node *)grammar->rules[node->value.root];
    if (!rule) {
      if(globals.logging_callback) {
        globals.logging_callback(grammar, CSPEECH_LOG_INFO, "Root rule not found: %s\n", node->value.root);
      }
      return 0;
    }
    grammar->root_rule = rule;
  }

  if (node->type == SNT_UNRESOLVED_REF) {
    /* resolve reference to local rule- drop first character # from URI */
    struct srgs_node *rule = (struct srgs_node *)grammar->rules[node->value.ref.uri + 1];
    if (!rule) {
      if(globals.logging_callback) {
        globals.logging_callback(grammar, CSPEECH_LOG_INFO, "Local rule not found: %s\n", node->value.ref.uri);
      }
      return 0;
    }

    /* link to rule */
    node->type = SNT_REF;
    node->value.ref.node = rule;
  }

  /* travel through rule to detect loops */
  if (node->type == SNT_REF) {
    if (!resolve_refs(grammar, node->value.ref.node, level + 1)) {
      return 0;
    }
  }

  /* resolve children refs */
  if (node->child) {
    struct srgs_node *child = node->child;
    for (; child; child = child->next) {
      if (!resolve_refs(grammar, child, level + 1)) {
        return 0;
      }
    }
  }

  node->visited = 0;
  sn_log_node_close(node);
  return 1;
}

/**
 * Parse the document into rules to match
 * @param parser the parser
 * @param document the document to parse
 * @return the parsed grammar if successful
 */
struct srgs_grammar *srgs_parse(struct srgs_parser *parser, const char *document)
{
  struct srgs_grammar *grammar = NULL;
  if (!parser) {
    if(globals.logging_callback) {
      globals.logging_callback(NULL, CSPEECH_LOG_CRIT, "NULL parser!!\n");
    }
    return NULL;
  }

  if (cspeech_zstr(document)) {
    if(globals.logging_callback) {
      globals.logging_callback(parser, CSPEECH_LOG_INFO, "Missing grammar document\n");
    }
    return NULL;
  }

  /* check for cached grammar */
  switch_mutex_lock(parser->mutex);
  grammar = (struct srgs_grammar *)switch_core_hash_find(parser->cache, document);
  if (!grammar) {
    int result = 0;
    iksparser *p;
    if(globals.logging_callback) {
      globals.logging_callback(parser, CSPEECH_LOG_DEBUG, "Parsing new grammar\n");
    }
    grammar = srgs_grammar_new(parser);
    p = iks_sax_new(grammar, tag_hook, cdata_hook);
    if (iks_parse(p, document, 0, 1) == IKS_OK) {
      if (grammar->root) {
        if(globals.logging_callback) {
          globals.logging_callback(parser, CSPEECH_LOG_DEBUG, "Resolving references\n");
        }
        if (resolve_refs(grammar, grammar->root, 0)) {
          result = 1;
        }
      } else {
        if(globals.logging_callback) {
          globals.logging_callback(parser, CSPEECH_LOG_INFO, "Nothing to parse!\n");
        }
      }
    }
    iks_parser_delete(p);
    if (result) {
      switch_core_hash_insert(parser->cache, document, grammar);
    } else {
      if (grammar) {
        srgs_grammar_destroy(grammar);
        grammar = NULL;
      }
      if(globals.logging_callback) {
        globals.logging_callback(parser, CSPEECH_LOG_INFO, "Failed to parse grammar\n");
      }
    }
  } else {
    if(globals.logging_callback) {
      globals.logging_callback(parser, CSPEECH_LOG_DEBUG, "Using cached grammar\n");
    }
  }
  switch_mutex_unlock(parser->mutex);

  return grammar;
}

#define MAX_INPUT_SIZE 128
#define OVECTOR_SIZE MAX_TAGS
#define WORKSPACE_SIZE 1024

/**
 * Check if no more digits can be added to input and match
 * @param compiled_regex the regex used in the initial match
 * @param input the input to check
 * @return true if end of match (no more input can be added)
 */
static int is_match_end(pcre *compiled_regex, const char *input)
{
  int ovector[OVECTOR_SIZE];
  int input_size = strlen(input);
  char search_input[MAX_INPUT_SIZE + 2];
  const char *search_set = "0123456789#*ABCD";
  const char *search = strchr(search_set, input[input_size - 1]); /* start with last digit in input */
  int i = 0;

  /* For each digit in search_set, check if input + search_set digit is a potential match.
     If so, then this is not a match end.
   */
  sprintf(search_input, "%sZ", input);
  for (i = 0; i < 16; i++) {
    int result;
    if (!*search) {
      search = search_set;
    }
    search_input[input_size] = *search++;
    result = pcre_exec(compiled_regex, NULL, search_input, input_size + 1, 0, 0,
      ovector, sizeof(ovector) / sizeof(ovector[0]));
    if (result > 0) {
      if(globals.logging_callback) {
        globals.logging_callback(NULL, CSPEECH_LOG_DEBUG, "not match end\n");
      }
      return 0;
    }
  }
  if(globals.logging_callback) {
    globals.logging_callback(NULL, CSPEECH_LOG_DEBUG, "is match end\n");
  }
  return 1;
}

/**
 * Find a match
 * @param grammar the grammar to match
 * @param input the input to compare
 * @param interpretation the (optional) interpretation of the input result
 * @return the match result
 */
enum srgs_match_type srgs_grammar_match(struct srgs_grammar *grammar, const char *input, const char **interpretation)
{
  int result = 0;
  int ovector[OVECTOR_SIZE];
  pcre *compiled_regex;

  *interpretation = NULL;

  if (cspeech_zstr(input)) {
    return SMT_NO_MATCH;
  }
  if (strlen(input) > MAX_INPUT_SIZE) {
    if(globals.logging_callback) {
      globals.logging_callback(NULL, CSPEECH_LOG_WARNING, "input too large: %s\n", input);
    }
    return SMT_NO_MATCH;
  }

  if (!(compiled_regex = get_compiled_regex(grammar))) {
    return SMT_NO_MATCH;
  }
  result = pcre_exec(compiled_regex, NULL, input, strlen(input), 0, PCRE_PARTIAL,
    ovector, OVECTOR_SIZE);

  if(globals.logging_callback) {
    globals.logging_callback(NULL, CSPEECH_LOG_DEBUG, "match = %i\n", result);
  }
  if (result > 0) {
    int i;
    char buffer[MAX_INPUT_SIZE + 1];
    buffer[MAX_INPUT_SIZE] = '\0';

    /* find matching instance... */
    for (i = 1; i <= grammar->tag_count; i++) {
      char substring_name[16] = { 0 };
      buffer[0] = '\0';
      snprintf(substring_name, 16, "%d", i);
      if (pcre_copy_named_substring(compiled_regex, input, ovector, result, substring_name, buffer, MAX_INPUT_SIZE) != PCRE_ERROR_NOSUBSTRING && !zstr_buf(buffer)) {
        *interpretation = grammar->tags[i];
        break;
      }
    }

    if (is_match_end(compiled_regex, input)) {
      return SMT_MATCH_END;
    }
    return SMT_MATCH;
  }
  if (result == PCRE_ERROR_PARTIAL) {
    return SMT_MATCH_PARTIAL;
  }

  return SMT_NO_MATCH;
}

/**
 * Generate regex from SRGS document.  Call this after parsing SRGS document.
 * @param parser the parser
 * @return the regex or NULL
 */
const char *srgs_grammar_to_regex(struct srgs_grammar *grammar)
{
  if (!grammar) {
    if(globals.logging_callback) {
      globals.logging_callback(NULL, CSPEECH_LOG_INFO, "grammar is NULL!\n");
    }
    return NULL;
  }
  switch_mutex_lock(grammar->mutex);
  if (!grammar->regex && !create_regexes(grammar, grammar->root, NULL)) {
    switch_mutex_unlock(grammar->mutex);
    return NULL;
  }
  switch_mutex_unlock(grammar->mutex);
  return grammar->regex;
}

/**
 * Create JSGF grammar
 * @param parser the parser
 * @param node root node
 * @param stream set to NULL
 * @return 1 if successful
 */
static int create_jsgf(struct srgs_grammar *grammar, struct srgs_node *node, switch_stream_handle_t *stream)
{
  sn_log_node_open(node);
  switch (node->type) {
    case SNT_GRAMMAR:
      if (node->child) {
        struct srgs_node *child;
        switch_stream_handle_t new_stream = { 0 };
        SWITCH_STANDARD_STREAM(new_stream);

        new_stream.write_function(&new_stream, "#JSGF V1.0");
        if (!cspeech_zstr(grammar->encoding)) {
          new_stream.write_function(&new_stream, " %s", grammar->encoding);
          if (!cspeech_zstr(grammar->language)) {
            new_stream.write_function(&new_stream, " %s", grammar->language);
          }
        }

        new_stream.write_function(&new_stream,
          ";\ngrammar org.freeswitch.srgs_to_jsgf;\n"
          "public ");

        /* output root rule */
        if (grammar->root_rule) {
          if (!create_jsgf(grammar, grammar->root_rule, &new_stream)) {
            switch_safe_free(new_stream.data);
            return 0;
          }
        } else {
          int num_rules = 0;
          int first = 1;

          for (child = node->child; child; child = child->next) {
            if (child->type == SNT_RULE && child->value.rule.is_public) {
              num_rules++;
            }
          }

          if (num_rules > 1) {
            new_stream.write_function(&new_stream, "<root> =");
            for (child = node->child; child; child = child->next) {
              if (child->type == SNT_RULE && child->value.rule.is_public) {
                if (!first) {
                  new_stream.write_function(&new_stream, "%s", " |");
                }
                first = 0;
                new_stream.write_function(&new_stream, " <%s>", child->value.rule.id);
              }
            }
            new_stream.write_function(&new_stream, ";\n");
          } else {
            for (child = node->child; child; child = child->next) {
              if (child->type == SNT_RULE && child->value.rule.is_public) {
                grammar->root_rule = child;
                if (!create_jsgf(grammar, child, &new_stream)) {
                  switch_safe_free(new_stream.data);
                  return 0;
                } else {
                  break;
                }
              }
            }
          }
        }

        /* output all rule definitions */
        for (child = node->child; child; child = child->next) {
          if (child->type == SNT_RULE && child != grammar->root_rule) {
            if (!create_jsgf(grammar, child, &new_stream)) {
              switch_safe_free(new_stream.data);
              return 0;
            }
          }
        }
        grammar->jsgf = strdup(new_stream.data);
        switch_safe_free(new_stream.data);
        if(globals.logging_callback) {
          globals.logging_callback(NULL, CSPEECH_LOG_DEBUG, "document jsgf = %s\n", grammar->jsgf);
        }
      }
      break;
    case SNT_RULE:
      if (node->child) {
        struct srgs_node *item = node->child;
        stream->write_function(stream, "<%s> =", node->value.rule.id);
        for (; item; item = item->next) {
          if (!create_jsgf(grammar, item, stream)) {
            if(globals.logging_callback) {
              globals.logging_callback(NULL, CSPEECH_LOG_DEBUG, "%s jsgf rule failed\n", node->value.rule.id);
            }
            return 0;
          }
        }
        stream->write_function(stream, ";\n");
      }
      break;
    case SNT_STRING: {
      int len = strlen(node->value.string);
      int i;
      stream->write_function(stream, " ");
      for (i = 0; i < len; i++) {
        switch (node->value.string[i]) {
          case '\\':
          case '*':
          case '+':
          case '/':
          case '(':
          case ')':
          case '[':
          case ']':
          case '{':
          case '}':
          case '=':
          case '<':
          case '>':
          case ';':
          case '|':
            stream->write_function(stream, "\\");
            break;
          default:
            break;
        }
        stream->write_function(stream, "%c", node->value.string[i]);
      }
      if (node->child) {
        if (!create_jsgf(grammar, node->child, stream)) {
          return 0;
        }
      }
      break;
    }
    case SNT_ITEM:
      if (node->child) {
        struct srgs_node *item;
        if (node->value.item.repeat_min == 0 && node->value.item.repeat_max == 1) {
          /* optional item */
          stream->write_function(stream, " [");
          for(item = node->child; item; item = item->next) {
            if (!create_jsgf(grammar, item, stream)) {
              return 0;
            }
          }
          stream->write_function(stream, " ]");
        } else {
          /* minimum repeats */
          int i;
          for (i = 0; i < node->value.item.repeat_min; i++) {
            if (node->value.item.repeat_min != 1 && node->value.item.repeat_max != 1) {
              stream->write_function(stream, " (");
            }
            for(item = node->child; item; item = item->next) {
              if (!create_jsgf(grammar, item, stream)) {
                return 0;
              }
            }
            if (node->value.item.repeat_min != 1 && node->value.item.repeat_max != 1) {
              stream->write_function(stream, " )");
            }
          }
          if (node->value.item.repeat_max == INT_MAX) {
            stream->write_function(stream, "*");
          } else {
            for (;i < node->value.item.repeat_max; i++) {
              stream->write_function(stream, " [");
              for(item = node->child; item; item = item->next) {
                if (!create_jsgf(grammar, item, stream)) {
                  return 0;
                }
              }
              stream->write_function(stream, " ]");
            }
          }
        }
      }
      break;
    case SNT_ONE_OF:
      if (node->child) {
        struct srgs_node *item = node->child;
        if (node->num_children > 1) {
          stream->write_function(stream, " (");
        }
        for (; item; item = item->next) {
          if (item != node->child) {
            stream->write_function(stream, " |");
          }
          stream->write_function(stream, " (");
          if (!create_jsgf(grammar, item, stream)) {
            return 0;
          }
          stream->write_function(stream, " )");
        }
        if (node->num_children > 1) {
          stream->write_function(stream, " )");
        }
      }
      break;
    case SNT_REF: {
      struct srgs_node *rule = node->value.ref.node;
      stream->write_function(stream, " <%s>", rule->value.rule.id);
      break;
    }
    case SNT_ANY:
    default:
      /* ignore */
      return 1;
  }
  sn_log_node_close(node);
  return 1;
}

/**
 * Generate JSGF from SRGS document.  Call this after parsing SRGS document.
 * @param grammar the grammar
 * @return the JSGF document or NULL
 */
const char *srgs_grammar_to_jsgf(struct srgs_grammar *grammar)
{
  if (!grammar) {
    if(globals.logging_callback) {
      globals.logging_callback(NULL, CSPEECH_LOG_CRIT, "grammar is NULL!\n");
    }
    return NULL;
  }
  switch_mutex_lock(grammar->mutex);
  if (!grammar->jsgf && !create_jsgf(grammar, grammar->root, NULL)) {
    switch_mutex_unlock(grammar->mutex);
    return NULL;
  }
  switch_mutex_unlock(grammar->mutex);
  return grammar->jsgf;
}

/**
 * Generate JSGF file from SRGS document.  Call this after parsing SRGS document.
 * @param grammar the grammar
 * @param basedir the base path to use if file does not already exist
 * @param ext the extension to use
 * @return the path or NULL
 */
const char *srgs_grammar_to_jsgf_file(struct srgs_grammar *grammar, const char *basedir, const char *ext)
{
  if (!grammar) {
    if(globals.logging_callback) {
      globals.logging_callback(grammar, CSPEECH_LOG_CRIT, "grammar is NULL!\n");
    }
    return NULL;
  }
  switch_mutex_lock(grammar->mutex);
  if (!grammar->jsgf_file_name) {
    char file_name_buf[SWITCH_UUID_FORMATTED_LENGTH + 1];
    switch_file_t *file;
    switch_size_t len;
    const char *jsgf = srgs_grammar_to_jsgf(grammar);
                switch_uuid_str(file_name_buf, sizeof(file_name_buf));
    grammar->jsgf_file_name = switch_core_sprintf(grammar->pool, "%s%s%s.%s", basedir, SWITCH_PATH_SEPARATOR, file_name_buf, ext);
    if (!jsgf) {
      switch_mutex_unlock(grammar->mutex);
      return NULL;
    }

    /* write grammar to file */
    if (switch_file_open(&file, grammar->jsgf_file_name, SWITCH_FOPEN_WRITE | SWITCH_FOPEN_TRUNCATE | SWITCH_FOPEN_CREATE, SWITCH_FPROT_OS_DEFAULT, grammar->pool) != SWITCH_STATUS_SUCCESS) {
      if(globals.logging_callback) {
        globals.logging_callback(NULL, CSPEECH_LOG_WARNING, "Failed to create jsgf file: %s!\n", grammar->jsgf_file_name);
      }
      grammar->jsgf_file_name = NULL;
      switch_mutex_unlock(grammar->mutex);
      return NULL;
    }
    len = strlen(jsgf);
    switch_file_write(file, jsgf, &len);
    switch_file_close(file);
  }
  switch_mutex_unlock(grammar->mutex);
  return grammar->jsgf_file_name;
}

/**
 * Initialize SRGS parser.  This function is not thread safe.
 */
int srgs_init(void)
{
  if (globals.init) {
    return 1;
  }

  globals.init = true;
  globals.logging_callback = NULL;
  switch_core_new_memory_pool(&globals.pool);

  add_root_tag_def("grammar", process_grammar, process_cdata_bad, "meta,metadata,lexicon,tag,rule");
  add_tag_def("ruleref", process_ruleref, process_cdata_bad, "");
  add_tag_def("token", process_attribs_ignore, process_cdata_ignore, "");
  add_tag_def("tag", process_attribs_ignore, process_cdata_tag, "");
  add_tag_def("one-of", process_attribs_ignore, process_cdata_tokens, "item");
  add_tag_def("item", process_item, process_cdata_tokens, "token,ruleref,item,one-of,tag");
  add_tag_def("rule", process_rule, process_cdata_tokens, "token,ruleref,item,one-of,tag,example");
  add_tag_def("example", process_attribs_ignore, process_cdata_ignore, "");
  add_tag_def("lexicon", process_attribs_ignore, process_cdata_bad, "");
  add_tag_def("meta", process_attribs_ignore, process_cdata_bad, "");
  add_tag_def("metadata", process_attribs_ignore, process_cdata_ignore, "ANY");
  add_tag_def("ANY", process_attribs_ignore, process_cdata_ignore, "ANY");

  return 1;
}

/* For Emacs:
 * Local Variables:
 * mode:c
 * indent-tabs-mode:t
 * tab-width:4
 * c-basic-offset:4
 * End:
 * For VIM:
 * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet:
 */


text:
<?php
class Kwf_Component_Model_Row extends Kwf_Model_Row_Abstract
{
    protected $_data;
    private $_tableLoaded = false;
    private $_setValues = array();

    public function __construct(array $config)
    {
        $this->_data = $config['data'];
        parent::__construct($config);
    }

    public function __isset($name)
    {
        return isset($this->_data->$name);
    }

    public function __unset($name)
    {
        unset($this->_data->$name);
    }

    public function __get($name)
    {
        $fields = array('parent_id', 'pos', 'visible', 'is_home');
        if (!$this->_tableLoaded &&
            in_array($name, $fields) &&
            is_numeric($this->componentId)
        ) {
            $m = $this->_data->generator->getModel();
            if (isset($this->_data->row) && $row = $m->getRow($this->_data->row->id)) {
                foreach ($fields as $field) {
                    $this->_data->$field = $row->$field;
                }
            }
            $this->_tableLoaded = true;
        }
        if ($name == 'id') $name = 'componentId';
        if (isset($this->_data->$name)) {
            return $this->_data->$name;;
        } else if ($name == 'parent_id' && $this->_data->parent) {
            return $this->_data->parent->componentId;
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        $this->_data->$name = $value;
        $this->_setValues[] = $name;
    }

    protected function _saveWithoutResetDirty()
    {
        $this->_beforeSave();

        if (!$this->_setValues) return; //nothing changed

        if ($this->_data instanceof Kwf_Component_Data_Root) {
            return;
        } else if ($this->_data->generator instanceof Kwc_Root_Category_Generator) {
            $id = $this->_data->dbId;
            $row = $this->_data->generator->getModel()->getRow($id);
        } else {
            $m = $this->_data->row->getModel();
            $primaryKey = $m->getPrimaryKey();
            $id = $this->_data->row->$primaryKey;
            if ($id) {
                $this->_beforeUpdate();
                $row = $this->_data->row;
            } else {
                $this->_beforeInsert();
                $row = $m->createRow();
            }
        }
        foreach ($this->_setValues as $key) {
            if ($key == 'visible' && !($this->_data->generator instanceof Kwc_Root_Category_Generator)) {
                $this->_data->generator->setVisible($this->_data, $this->_data->visible);
            } else {
                $row->$key = $this->_data->$key;
            }
        }
        $ret = $row->save();
        if ($id) {
            $this->_afterUpdate();
        } else {
            $this->_afterInsert();
        }
        $this->_afterSave();
        return $ret;
    }

    protected function _afterSave()
    {
        parent::_afterSave();
        Kwf_Component_Data_Root::reset();
        Kwf_Component_Generator_Abstract::clearInstances();
    }

    public function delete()
    {
        $this->_beforeDelete();
        $m = $this->_data->generator->getModel();
        $m->getRow($this->_data->row->id)->delete();
        $this->_afterDelete();
    }

    public function getData()
    {
        return $this->_data;
    }

    public function toArray()
    {
        return array();
    }
}

redpajama_set_name: RedPajamaGithub


text:
API of Getinng Serving Job Details can get the serving job details.

## Path

pkg/apis/arenaclient.ServingJobClient

## Function

	func (t *ServingJobClient) Get(jobName, version string, jobType types.ServingJobType) (*types.ServingJobInfo, error)

## Parameters

* jobName(type: string) => the name of serving job
* version(type string) => the version of serving job,if the version is null(""),match the all versions of the job.
* jobType(type: pkg/apis/types.ServingJobType) = > specify the serving job type

## Example

### serving job type is unknown

	package main
	import(
		"fmt"
		"github.com/kubeflow/arena/pkg/apis/arenaclient"
		"github.com/kubeflow/arena/pkg/apis/types"
	)

	func main() {
		// create the arena client
		client, err := arenaclient.NewArenaClient(types.ArenaClientArgs{
			Kubeconfig:     "",
			LogLevel:      	"debug",
			Namespace:      "",
			ArenaNamespace: "",
			IsDaemonMode:   false,
		})
		if err != nil {
			fmt.Printf("failed to build arena client.,reason: %v",err)
			return
		}
		// get the serving job which name is test and its' type is unknown
		servingJob,err := client.Serving().Get("test","",types.AllServingJob)
        if err != nil {
            fmt.Printf("failed to get serving job,reason: %v",err)
            return
        }
        fmt.Printf("serving job instances: %v\n",servingJob.Instances)
	}

### serving job type is known

	package main
	import(
		"fmt"
		"github.com/kubeflow/arena/pkg/apis/arenaclient"
		"github.com/kubeflow/arena/pkg/apis/types"
	)

	func main() {
		// create the arena client
		client, err := arenaclient.NewArenaClient(types.ArenaClientArgs{
			Kubeconfig:     "",
			LogLevel:      	"debug",
			Namespace:      "",
			ArenaNamespace: "",
			IsDaemonMode:   false,
		})
		if err != nil {
			fmt.Printf("failed to build arena client.,reason: %v",err)
			return
		}
		// get the serving job which name is test and its' type is custom serving job
		servingJob,err := client.Serving().Get("test","",types.CustomServingJob))
        if err != nil {
            fmt.Printf("failed to get serving job,reason: %v",err)
            return
        }
        fmt.Printf("serving job Instances: %v\n",servingJob.Instances)
	}


text:
package com.example.miraclewong.customview;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.view.View;

/**
 * Created by miraclewong on 2016/1/26.
 */
public class MyPathViewWaveCurve extends View{
    private int width;//设置高
    private int height;//设置高

    private Path mPath;
    private Paint mPaint;
    private Paint mPaintCircle;

    private int count = 0;
    private static final int NEED_INVALIDATE = 0X6666;
    //操作UI主线程
    private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what) {
                case NEED_INVALIDATE:
                    //更新时间
                    count += 5;
                    if (count > 80) {
                        count = 0;
                    }
                    invalidate();
                    sendEmptyMessageDelayed(NEED_INVALIDATE, 50);
                    break;
            }
        }
    };

    public MyPathViewWaveCurve(Context context) {
        super(context);
    }

    public MyPathViewWaveCurve(Context context, AttributeSet attrs) {
        super(context, attrs);
        // 初始化path
        mPath = new Path();
        //初始化画笔
        mPaint = new Paint();
        mPaint.setAntiAlias(true); //抗锯齿
        mPaint.setTextSize(30);
        mPaint.setColor(Color.BLACK);
        mPaint.setStrokeWidth(3);
        mPaint.setStyle(Paint.Style.STROKE);//非填充

        //初始化画点的画笔
        mPaintCircle = new Paint();
        mPaintCircle.setAntiAlias(true);
        mPaintCircle.setColor(Color.GRAY);
        mPaintCircle.setStyle(Paint.Style.STROKE);
        mPaintCircle.setStrokeWidth(10);
        handler.sendEmptyMessageDelayed(NEED_INVALIDATE, 50);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        width = getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec);
        height = getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec);
        setMeasuredDimension(width, height);//设置宽和高
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //绘制波浪形曲线
        mPath.reset();
        mPath.moveTo(count, height / 2);
        for (int i = 0; i < 10; i++) {
            mPath.rQuadTo(20, 5, 40, 0);//rQuadTo()方法是以当前点为起始点,将其视为(0,0)点绘制。这里也就是将
            mPath.rQuadTo(20, -5, 40, 0);
        }
        canvas.drawPath(mPath, mPaint);
        canvas.drawCircle(width / 2, height / 2, 50, mPaintCircle);

    }
}


text:
using System;
using LavaLeak.Diplomata.Models;
using LavaLeak.Diplomata.Persistence;
using UnityEngine;

namespace LavaLeak.Diplomata.Persistence
{
  /// <summary>
  /// The abstract class to get the persistent models data.
  /// </summary>
  [Serializable]
  abstract public class Persistent
  {
    /// <summary>
    /// Override to return the object as json.
    /// </summary>
    /// <returns>A json string.</returns>
    public override string ToString()
    {
      return JsonUtility.ToJson(this);
    }
  }
}


text:
layout: default
title: Aphorisms and such
---

Aphorisms and such

  {% for ps in site.categories.aphorisms %}

      {{ ps.content }}

  {% endfor %}


text:
package org.example.calculator;

import griffon.core.artifact.GriffonModel;
import griffon.metadata.ArtifactProviderFor;
import org.codehaus.griffon.runtime.core.artifact.AbstractGriffonModel;

@ArtifactProviderFor(GriffonModel.class)
public class CalculatorModel extends AbstractGriffonModel {
    private String op1;
    private String op2;
    private String result;

    public String getOp1() {
        return op1;
    }

    public void setOp1(String op1) {
        firePropertyChange("op1", this.op1, this.op1 = op1);
    }

    public String getOp2() {
        return op2;
    }

    public void setOp2(String op2) {
        firePropertyChange("op2", this.op2, this.op2 = op2);
    }

    public String getResult() {
        return result;
    }

    public void setResult(String result) {
        firePropertyChange("result", this.op2, this.result = result);
    }
}


text:
package main

import (
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
)

type BzrScm struct {
	name     string
	url      string
	workDir  string
	exePath  string
	revision string
}

func NewBzrScm(name, url, workDir, revision string) (*BzrScm, error) {
	exePath, err := exec.LookPath("bzr")
	if err != nil {
		return nil, err
	}
	return &BzrScm{
		workDir:  workDir,
		name:     name,
		url:      url,
		exePath:  exePath,
		revision: revision,
	}, nil
}

func (s *BzrScm) runBzrCommand(command ...string) error {
	bzrCommands := command
	bzrCommands = append(bzrCommands, s.FullPath())

	cmd := exec.Command(s.exePath, bzrCommands...)
	err := cmd.Run()
	if err != nil {
		return err
	}
	return nil
}

func (s *BzrScm) FullPath() string {
	return filepath.Join(s.workDir, s.name)
}

func (s *BzrScm) exists() bool {
	if _, err := os.Stat(s.FullPath()); os.IsNotExist(err) {
		return false
	}
	return true
}

func (s *BzrScm) initWorkdir() error {
	if _, err := os.Stat(s.workDir); os.IsNotExist(err) {
		err := os.MkdirAll(s.workDir, 00755)
		if err != nil {
			return err
		}
	}
	return nil
}

func (s *BzrScm) gotoRevision(revision string) error {
	var err error = nil
	if revision != "" {
		err = s.runBzrCommand("revert", "-r", revision)
	} else {
		err = s.runBzrCommand("revert")
	}
	if err != nil {
		return err
	}
	return nil
}

func (s *BzrScm) clone() error {
	if s.exists() {
		return fmt.Errorf("Repository %s already exists", s.FullPath())
	}
	cmd := exec.Command(s.exePath, "branch", s.url, s.FullPath())
	out, err := cmd.CombinedOutput()
	if err != nil {
		return fmt.Errorf("Error running git clone: %s --> %s", err, out)
	}
	return nil
}

func (s *BzrScm) update(revision string) error {
	fmt.Println("Updating repository: " + s.FullPath())
	exists := s.exists()
	if !exists {
		err := s.clone()
		if err != nil {
			return err
		}
	} else {
		isClean, err := s.isClean()
		if err != nil {
			return err
		}
		if !isClean {
			return fmt.Errorf("%s is not clean. Please commit local changes", s.FullPath())
		}
	}
	err := s.gotoRevision(revision)
	if err != nil {
		return err
	}
	return nil
}

func (s *BzrScm) isClean() (bool, error) {
	exists := s.exists()
	if !exists {
		return false, fmt.Errorf("Repository %s does not exists", s.FullPath())
	}
	cmd := exec.Command(s.exePath, "status", s.FullPath())
	out, err := cmd.CombinedOutput()
	if err != nil {
		return false, err
	}
	if string(out) != "" {
		return false, nil
	}
	return true, nil
}

func (s *BzrScm) Name() string {
	return s.name
}

func (s *BzrScm) Update() error {
	err := s.initWorkdir()
	if err != nil {
		return err
	}
	err = s.update(s.revision)
	return err
}


text:
package org.frameworkset.web.servlet.view;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import javax.activation.FileTypeMap;
import javax.activation.MimetypesFileTypeMap;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.frameworkset.http.MediaType;
import org.frameworkset.util.Assert;
import org.frameworkset.util.ClassUtils;
import org.frameworkset.util.CollectionUtils;
import org.frameworkset.util.io.ClassPathResource;
import org.frameworkset.util.io.Resource;
import org.frameworkset.web.servlet.context.RequestAttributes;
import org.frameworkset.web.servlet.context.RequestContextHolder;
import org.frameworkset.web.servlet.context.ServletRequestAttributes;
import org.frameworkset.web.servlet.support.WebApplicationObjectSupport;
import org.frameworkset.web.util.UrlPathHelper;
import org.frameworkset.web.util.WebUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.frameworkset.util.StringUtil;

/**
 * Implementation of {@link ViewResolver} that resolves a view based on the request file name or {@code Accept} header.
 *
 *
The {@code ContentNegotiatingViewResolver} does not resolve views itself, but delegates to other {@link
 * ViewResolver}s. By default, these other view resolvers are picked up automatically from the application context,
 * though they can also be set explicitly by using the {@link #setViewResolvers(List) viewResolvers} property.
 * Note that in order for this view resolver to work properly, the {@link #setOrder(int) order}
 * property needs to be set to a higher precedence than the others (the default is {@link Ordered#HIGHEST_PRECEDENCE}.)
 *
 *
This view resolver uses the requested {@linkplain MediaType media type} to select a suitable {@link View} for a
 * request. This media type is determined by using the following criteria:
 *

 *
If the requested path has a file extension and if the {@link #setFavorPathExtension(boolean)} property is
 * {@code true}, the {@link #setMediaTypes(Map) mediaTypes} property is inspected for a matching media type.

 *
If the request contains a parameter defining the extension and if the {@link #setFavorParameter(boolean)}
 * property is true, the {@link #setMediaTypes(Map) mediaTypes} property is inspected for a matching
 * media type. The default name of the parameter is format and it can be configured using the
 * {@link #setParameterName(String) parameterName} property.

 *
If there is no match in the {@link #setMediaTypes(Map) mediaTypes} property and if the Java Activation
 * Framework (JAF) is both {@linkplain #setUseJaf(boolean) enabled} and present on the class path,
 * {@link FileTypeMap#getContentType(String)} is used instead.

 *
If the previous steps did not result in a media type, and
 * {@link #setIgnoreAcceptHeader(boolean) ignoreAcceptHeader} is {@code false}, the request {@code Accept} header is
 * used.

 *

 *
 * Once the requested media type has been determined, this resolver queries each delegate view resolver for a
 * {@link View} and determines if the requested media type is {@linkplain MediaType#includes(MediaType) compatible}
 * with the view's {@linkplain View#getContentType() content type}). The most compatible view is returned.
 *
 *
Additionally, this view resolver exposes the {@link #setDefaultViews(List) defaultViews} property, allowing you to
 * override the views provided by the view resolvers. Note that these default views are offered as candicates, and
 * still need have the content type requested (via file extension, parameter, or {@code Accept} header, described above).
 * You can also set the {@linkplain #setDefaultContentType(MediaType) default content type} directly, which will be
 * returned when the other mechanisms ({@code Accept} header, file extension or parameter) do not result in a match.
 *
 *
For example, if the request path is {@code /view.html}, this view resolver will look for a view that has the
 * {@code text/html} content type (based on the {@code html} file extension). A request for {@code /view} with a {@code
 * text/html} request {@code Accept} header has the same result.
 *
 * @author Arjen Poutsma
 * @author Juergen Hoeller
 * @since 3.0
 * @see ViewResolver
 * @see InternalResourceViewResolver
 * @see BeanNameViewResolver
 */
public class ContentNegotiatingViewResolver extends WebApplicationObjectSupport implements ViewResolver {

	private final static Logger logger = LoggerFactory.getLogger(ContentNegotiatingViewResolver.class);

	private static final String ACCEPT_HEADER = "Accept";

	private static final boolean jafPresent =
			ClassUtils.isPresent("javax.activation.FileTypeMap", ContentNegotiatingViewResolver.class.getClassLoader());

	private static final UrlPathHelper urlPathHelper = new UrlPathHelper();

	private boolean favorPathExtension = true;

	private boolean favorParameter = false;

	private String parameterName = "format";

	private boolean useNotAcceptableStatusCode = false;

	private boolean ignoreAcceptHeader = false;

	private boolean useJaf = true;

	private ConcurrentMap<String, MediaType> mediaTypes = new ConcurrentHashMap<String, MediaType>();

	private List<View> defaultViews;

	private MediaType defaultContentType;

	private List<ViewResolver> viewResolvers;

	/**
	 * Indicates whether the extension of the request path should be used to determine the requested media type,
	 * in favor of looking at the {@code Accept} header. The default value is {@code true}.
	 *
For instance, when this flag is true (the default), a request for {@code /hotels.pdf}
	 * will result in an {@code AbstractPdfView} being resolved, while the {@code Accept} header can be the
	 * browser-defined {@code text/html,application/xhtml+xml}.
	 */
	public void setFavorPathExtension(boolean favorPathExtension) {
		this.favorPathExtension = favorPathExtension;
	}

	/**
	 * Indicates whether a request parameter should be used to determine the requested media type,
	 * in favor of looking at the {@code Accept} header. The default value is {@code false}.
	 *
For instance, when this flag is true, a request for {@code /hotels?format=pdf} will result
	 * in an {@code AbstractPdfView} being resolved, while the {@code Accept} header can be the browser-defined
	 * {@code text/html,application/xhtml+xml}.
	 */
	public void setFavorParameter(boolean favorParameter) {
		this.favorParameter = favorParameter;
	}

	/**
	 * Sets the parameter name that can be used to determine the requested media type if the {@link
	 * #setFavorParameter(boolean)} property is {@code true}. The default parameter name is {@code format}.
	 */
	public void setParameterName(String parameterName) {
		this.parameterName = parameterName;
	}

	/**
	 * Indicates whether the HTTP {@code Accept} header should be ignored. Default is {@code false}.
	 * If set to {@code true}, this view resolver will only refer to the file extension and/or paramter,
	 * as indicated by the {@link #setFavorPathExtension(boolean) favorPathExtension} and
	 * {@link #setFavorParameter(boolean) favorParameter} properties.
	 */
	public void setIgnoreAcceptHeader(boolean ignoreAcceptHeader) {
		this.ignoreAcceptHeader = ignoreAcceptHeader;
	}

	/**
	 * Indicates whether a {@link HttpServletResponse#SC_NOT_ACCEPTABLE 406 Not Acceptable} status code should be
	 * returned if no suitable view can be found.
	 *
	 *
Default is {@code false}, meaning that this view resolver returns {@code null} for
	 * {@link #resolveViewName(String, Locale)} when an acceptable view cannot be found. This will allow for view
	 * resolvers chaining. When this property is set to {@code true},
	 * {@link #resolveViewName(String, Locale)} will respond with a view that sets the response status to
	 * {@code 406 Not Acceptable} instead.
	 */
	public void setUseNotAcceptableStatusCode(boolean useNotAcceptableStatusCode) {
		this.useNotAcceptableStatusCode = useNotAcceptableStatusCode;
	}

	/**
	 * Sets the mapping from file extensions to media types.
	 *
When this mapping is not set or when an extension is not present, this view resolver
	 * will fall back to using a {@link FileTypeMap} when the Java Action Framework is available.
	 */
	public void setMediaTypes(Map<String, String> mediaTypes) {
		Assert.notNull(mediaTypes, "'mediaTypes' must not be null");
		for (Map.Entry<String, String> entry : mediaTypes.entrySet()) {
			String extension = entry.getKey().toLowerCase(Locale.ENGLISH);
			MediaType mediaType = MediaType.parseMediaType(entry.getValue());
			this.mediaTypes.put(extension, mediaType);
		}
	}

	/**
	 * Sets the default views to use when a more specific view can not be obtained
	 * from the {@link ViewResolver} chain.
	 */
	public void setDefaultViews(List<View> defaultViews) {
		this.defaultViews = defaultViews;
	}

	/**
	 * Sets the default content type.
	 *
This content type will be used when file extension, parameter, nor {@code Accept}
	 * header define a content-type, either through being disabled or empty.
	 */
	public void setDefaultContentType(MediaType defaultContentType) {
		this.defaultContentType = defaultContentType;
	}

	/**
	 * Indicates whether to use the Java Activation Framework to map from file extensions to media types.
	 *
Default is {@code true}, i.e. the Java Activation Framework is used (if available).
	 */
	public void setUseJaf(boolean useJaf) {
		this.useJaf = useJaf;
	}

	/**
	 * Sets the view resolvers to be wrapped by this view resolver.
	 *
If this property is not set, view resolvers will be detected automatically.
	 */
	public void setViewResolvers(List<ViewResolver> viewResolvers) {
		this.viewResolvers = viewResolvers;
	}

	@Override
	protected void initServletContext(ServletContext servletContext) {
		if (this.viewResolvers == null) {
//			Map<String, ViewResolver> matchingBeans =
//					BeanFactoryUtils.beansOfTypeIncludingAncestors(getApplicationContext(), ViewResolver.class);
//			this.viewResolvers = new ArrayList<ViewResolver>(matchingBeans.size());
//			for (ViewResolver viewResolver : matchingBeans.values()) {
//				if (this != viewResolver) {
//					this.viewResolvers.add(viewResolver);
//				}
//			}
		}
		if (this.viewResolvers.isEmpty()) {
			logger.warn("Did not find any ViewResolvers to delegate to; please configure them using the " +
					"'viewResolvers' property on the ContentNegotiatingViewResolver");
		}
//		OrderComparator.sort(this.viewResolvers);
	}

	/**
	 * Determines the list of {@link MediaType} for the given {@link HttpServletRequest}.
	 *
The default implementation invokes {@link #getMediaTypeFromFilename(String)} if {@linkplain
	 * #setFavorPathExtension(boolean) favorPathExtension} property is true. If the property is
	 * false, or when a media type cannot be determined from the request path, this method will
	 * inspect the {@code Accept} header of the request.
	 *
This method can be overriden to provide a different algorithm.
	 * @param request the current servlet request
	 * @return the list of media types requested, if any
	 */
	protected List<MediaType> getMediaTypes(HttpServletRequest request) {
		if (this.favorPathExtension) {
			String requestUri = urlPathHelper.getRequestUri(request);
			String filename = WebUtils.extractFullFilenameFromUrlPath(requestUri);
			MediaType mediaType = getMediaTypeFromFilename(filename);
			if (mediaType != null) {
				if (logger.isDebugEnabled()) {
					logger.debug("Requested media type is '" + mediaType + "' (based on filename '" + filename + "')");
				}
				return Collections.singletonList(mediaType);
			}
		}
		if (this.favorParameter) {
			if (request.getParameter(this.parameterName) != null) {
				String parameterValue = request.getParameter(this.parameterName);
				MediaType mediaType = getMediaTypeFromParameter(parameterValue);
				if (mediaType != null) {
					if (logger.isDebugEnabled()) {
						logger.debug("Requested media type is '" + mediaType + "' (based on parameter '" +
								this.parameterName + "'='" + parameterValue + "')");
					}
					return Collections.singletonList(mediaType);
				}
			}
		}
		if (!this.ignoreAcceptHeader) {
			String acceptHeader = request.getHeader(ACCEPT_HEADER);
			if (StringUtil.hasText(acceptHeader)) {
				List<MediaType> mediaTypes = MediaType.parseMediaTypes(acceptHeader);
				MediaType.sortByQualityValue(mediaTypes);
				if (logger.isDebugEnabled()) {
					logger.debug("Requested media types are " + mediaTypes + " (based on Accept header)");
				}
				return mediaTypes;
			}
		}
		if (this.defaultContentType != null) {
			if (logger.isDebugEnabled()) {
				logger.debug("Requested media types is " + this.defaultContentType +
						" (based on defaultContentType property)");
			}
			return Collections.singletonList(this.defaultContentType);
		}
		else {
			return Collections.emptyList();
		}
	}

	/**
	 * Determines the {@link MediaType} for the given filename.
	 *
The default implementation will check the {@linkplain #setMediaTypes(Map) media types}
	 * property first for a defined mapping. If not present, and if the Java Activation Framework
	 * can be found on the classpath, it will call {@link FileTypeMap#getContentType(String)}
	 *
This method can be overriden to provide a different algorithm.
	 * @param filename the current request file name (i.e. {@code hotels.html})
	 * @return the media type, if any
	 */
	protected MediaType getMediaTypeFromFilename(String filename) {
		String extension = StringUtil.getFilenameExtension(filename);
		if (!StringUtil.hasText(extension)) {
			return null;
		}
		extension = extension.toLowerCase(Locale.ENGLISH);
		MediaType mediaType = this.mediaTypes.get(extension);
		if (mediaType == null && this.useJaf && jafPresent) {
			mediaType = ActivationMediaTypeFactory.getMediaType(filename);
			if (mediaType != null) {
				this.mediaTypes.putIfAbsent(extension, mediaType);
			}
		}
		return mediaType;
	}

	/**
	 * Determines the {@link MediaType} for the given parameter value.
	 *
The default implementation will check the {@linkplain #setMediaTypes(Map) media types}
	 * property for a defined mapping.
	 *
This method can be overriden to provide a different algorithm.
	 * @param parameterValue the parameter value (i.e. {@code pdf}).
	 * @return the media type, if any
	 */
	protected MediaType getMediaTypeFromParameter(String parameterValue) {
		return this.mediaTypes.get(parameterValue.toLowerCase(Locale.ENGLISH));
	}

	public View resolveViewName(String viewName, Locale locale) throws Exception {
		RequestAttributes attrs = RequestContextHolder.getRequestAttributes();
		Assert.isInstanceOf(ServletRequestAttributes.class, attrs);
		List<MediaType> requestedMediaTypes = getMediaTypes(((ServletRequestAttributes) attrs).getRequest());
		List<View> candidateViews = getCandidateViews(viewName, locale, requestedMediaTypes);
		View bestView = getBestView(candidateViews, requestedMediaTypes);
		if (bestView != null) {
			return bestView;
		}
		else {
			if (this.useNotAcceptableStatusCode) {
				if (logger.isDebugEnabled()) {
					logger.debug("No acceptable view found; returning 406 (Not Acceptable) status code");
				}
				return NOT_ACCEPTABLE_VIEW;
			}
			else {
				if (logger.isDebugEnabled()) {
					logger.debug("No acceptable view found; returning null");
				}
				return null;
			}
		}
	}

	private List<View> getCandidateViews(String viewName, Locale locale, List<MediaType> requestedMediaTypes)
			throws Exception {

		List<View> candidateViews = new ArrayList<View>();
		for (ViewResolver viewResolver : this.viewResolvers) {
			View view = viewResolver.resolveViewName(viewName, locale);
			if (view != null) {
				candidateViews.add(view);
			}
			for (MediaType requestedMediaType : requestedMediaTypes) {
				List<String> extensions = getExtensionsForMediaType(requestedMediaType);
				for (String extension : extensions) {
					String viewNameWithExtension = viewName + "." + extension;
					view = viewResolver.resolveViewName(viewNameWithExtension, locale);
					if (view != null) {
						candidateViews.add(view);
					}
				}

			}
		}
		if (!CollectionUtils.isEmpty(this.defaultViews)) {
			candidateViews.addAll(this.defaultViews);
		}
		return candidateViews;
	}

	private List<String> getExtensionsForMediaType(MediaType requestedMediaType) {
		List<String> result = new ArrayList<String>();
		for (Entry<String, MediaType> entry : mediaTypes.entrySet()) {
			if (requestedMediaType.includes(entry.getValue())) {
				result.add(entry.getKey());
			}
		}
		return result;
	}

	private View getBestView(List<View> candidateViews, List<MediaType> requestedMediaTypes) {
		MediaType bestRequestedMediaType = null;
		View bestView = null;
		for (MediaType requestedMediaType : requestedMediaTypes) {
			for (View candidateView : candidateViews) {
				if (StringUtil.hasText(candidateView.getContentType())) {
					MediaType candidateContentType = MediaType.parseMediaType(candidateView.getContentType());
					if (requestedMediaType.includes(candidateContentType)) {
						bestRequestedMediaType = requestedMediaType;
						bestView = candidateView;
						break;
					}
				}
			}
			if (bestView != null) {
				if (logger.isDebugEnabled()) {
					logger.debug(
							"Returning [" + bestView + "] based on requested media type '" + bestRequestedMediaType +
									"'");
				}
				break;
			}
		}
		return bestView;

	}

	/**
	 * Inner class to avoid hard-coded JAF dependency.
	 */
	private static class ActivationMediaTypeFactory {

		private static final FileTypeMap fileTypeMap;

		static {
			fileTypeMap = loadFileTypeMapFromContextSupportModule();
		}

		private static FileTypeMap loadFileTypeMapFromContextSupportModule() {
			// see if we can find the extended mime.types from the context-support module
			Resource mappingLocation = new ClassPathResource("org/frameworkset/web/servlet/mime.types");
			if (mappingLocation.exists()) {
//				if (logger.isTraceEnabled())
				{
					logger.info("Loading Java Activation Framework FileTypeMap from " + mappingLocation);
				}
				InputStream inputStream = null;
				try {
					inputStream = mappingLocation.getInputStream();
					return new MimetypesFileTypeMap(inputStream);
				}
				catch (IOException ex) {
					// ignore
				}
				finally {
					if (inputStream != null) {
						try {
							inputStream.close();
						}
						catch (IOException ex) {
							// ignore
						}
					}
				}
			}
//			if (logger.isTraceEnabled())
			{
				logger.info("Loading default Java Activation Framework FileTypeMap");
			}
			return FileTypeMap.getDefaultFileTypeMap();
		}

		public static MediaType getMediaType(String fileName) {
			String mediaType = fileTypeMap.getContentType(fileName);
			return StringUtil.hasText(mediaType) ? MediaType.parseMediaType(mediaType) : null;
		}
	}

	private static final View NOT_ACCEPTABLE_VIEW = new View() {

		public String getContentType() {
			return null;
		}

		public void render(Map model, HttpServletRequest request, HttpServletResponse response)
				throws Exception {
			response.setStatus(HttpServletResponse.SC_NOT_ACCEPTABLE);
		}
	};

}


text:
<?xml version='1.0' encoding='UTF-8' standalone='no'?>
<!DOCTYPE cxxClass PUBLIC "-//NOKIA//DTD DITA C++ API Class Reference Type v0.5.0//EN" "dtd/cxxClass.dtd" >
<cxxClass id="class_meta_1_1_t_meta_buf8">
	<apiName>Meta::TMetaBuf8</apiName>
	<cxxClassDetail>
		<cxxClassDefinition>
			<cxxClassAccessSpecifier value="public"/>
			<cxxClassDerivations>
				<cxxClassDerivation>
					<cxxClassDerivationAccessSpecifier value="public"/>
					<cxxClassBaseClass href="class_meta_1_1_t_meta_var_len8.xml#class_meta_1_1_t_meta_var_len8">Meta::TMetaVarLen8< RBuf8 ></cxxClassBaseClass>
				</cxxClassDerivation>
			</cxxClassDerivations>
			<cxxClassAPIItemLocation>
				<cxxClassDeclarationFile name="filePath" value="K:/epoc32/include/elements/metabuffer.h"/>
				<cxxClassDeclarationFileLine name="lineNumber" value="35"/>
				<cxxClassDefinitionFile name="filePath" value="K:/epoc32/include/elements/metabuffer.h"/>
				<cxxClassDefinitionFileLineStart name="lineNumber" value="27"/>
				<cxxClassDefinitionFileLineEnd name="lineNumber" value="46"/>
			</cxxClassAPIItemLocation>
		</cxxClassDefinition>
		<apiDesc>

Implementation of <xref href="class_meta_1_1_m_meta_type.xml#class_meta_1_1_m_meta_type">MMetaType</xref> for dynamicaly allocated <xref href="class_r_buf8.xml#class_r_buf8">RBuf8</xref>

since v9.0

		</apiDesc>
	</cxxClassDetail>
	<cxxFunction id="class_meta_1_1_t_meta_buf8_1a14f9a5f3beae8bdb0d96f2ae8aeb027d">
		<apiName>NewL</apiName>
		<cxxFunctionDetail>
			<cxxFunctionDefinition>
				<cxxFunctionAccessSpecifier value="public"/>
				<cxxFunctionStorageClassSpecifierStatic/>
				<cxxFunctionDeclaredType>IMPORT_C <apiRelation href="class_meta_1_1_m_meta_type.xml#class_meta_1_1_m_meta_type">MMetaType</apiRelation> *</cxxFunctionDeclaredType>
				<cxxFunctionScopedName>Meta::TMetaBuf8</cxxFunctionScopedName>
				<cxxFunctionPrototype>static IMPORT_C MMetaType * NewL(const TAny *aMem, const TAny *aData)</cxxFunctionPrototype>
				<cxxFunctionNameLookup>Meta::TMetaBuf8::NewL(const TAny *,const TAny *)</cxxFunctionNameLookup>
				<cxxFunctionReimplemented href="class_meta_1_1_t_meta_var_len8.xml#class_meta_1_1_t_meta_var_len8_1a52cad88f359dbc5188ac85a6ed9b2eff">TMetaVarLen8< RBuf8 >::NewL(const TAny *,const TAny *)</cxxFunctionReimplemented>
				<cxxFunctionParameters>
					<cxxFunctionParameter>
						<cxxFunctionParameterDeclaredType>const <apiRelation href="e32def_8h.xml#_1af7aafba448a6eaa6ce8801f88dcb5b90">TAny</apiRelation> *</cxxFunctionParameterDeclaredType>
						<cxxFunctionParameterDeclarationName>aMem</cxxFunctionParameterDeclarationName>
						<apiDefNote/>
					</cxxFunctionParameter>
					<cxxFunctionParameter>
						<cxxFunctionParameterDeclaredType>const <apiRelation href="e32def_8h.xml#_1af7aafba448a6eaa6ce8801f88dcb5b90">TAny</apiRelation> *</cxxFunctionParameterDeclaredType>
						<cxxFunctionParameterDeclarationName>aData</cxxFunctionParameterDeclarationName>
						<apiDefNote/>
					</cxxFunctionParameter>
				</cxxFunctionParameters>
				<cxxFunctionAPIItemLocation>
					<cxxFunctionDeclarationFile name="filePath" value="K:/epoc32/include/elements/metabuffer.h"/>
					<cxxFunctionDeclarationFileLine name="lineNumber" value="27"/>
				</cxxFunctionAPIItemLocation>
			</cxxFunctionDefinition>
			<apiDesc/>
		</cxxFunctionDetail>
	</cxxFunction>
	<cxxFunction id="class_meta_1_1_t_meta_buf8_1a0fd16fcdd6cf8bc57d92baec242a6f04">
		<apiName>Load</apiName>
		<cxxFunctionDetail>
			<cxxFunctionDefinition>
				<cxxFunctionAccessSpecifier value="public"/>
				<cxxFunctionVirtual/>
				<cxxFunctionDeclaredType>IMPORT_C <apiRelation href="e32def_8h.xml#_1abb88f5378e8305d934297176fe5fa298">TInt</apiRelation>
				</cxxFunctionDeclaredType>
				<cxxFunctionScopedName>Meta::TMetaBuf8</cxxFunctionScopedName>
				<cxxFunctionPrototype>virtual IMPORT_C TInt Load(TPtrC8 &aBuffer)</cxxFunctionPrototype>
				<cxxFunctionNameLookup>Meta::TMetaBuf8::Load(TPtrC8 &)</cxxFunctionNameLookup>
				<cxxFunctionReimplemented href="class_meta_1_1_t_meta_var_len_base.xml#class_meta_1_1_t_meta_var_len_base_1a31358edd9280eefc947ef31b6883ccb5">Meta::TMetaVarLenBase::Load(TPtrC8 &)</cxxFunctionReimplemented>
				<cxxFunctionParameters>
					<cxxFunctionParameter>
						<cxxFunctionParameterDeclaredType>
							<apiRelation href="class_t_ptr_c8.xml#class_t_ptr_c8">TPtrC8</apiRelation> &</cxxFunctionParameterDeclaredType>
						<cxxFunctionParameterDeclarationName>aBuffer</cxxFunctionParameterDeclarationName>
						<apiDefNote/>
					</cxxFunctionParameter>
				</cxxFunctionParameters>
				<cxxFunctionAPIItemLocation>
					<cxxFunctionDeclarationFile name="filePath" value="K:/epoc32/include/elements/metabuffer.h"/>
					<cxxFunctionDeclarationFileLine name="lineNumber" value="29"/>
				</cxxFunctionAPIItemLocation>
			</cxxFunctionDefinition>
			<apiDesc/>
		</cxxFunctionDetail>
	</cxxFunction>
	<cxxFunction id="class_meta_1_1_t_meta_buf8_1ae43ea680919464182ca685b7119da86a">
		<apiName>Store</apiName>
		<cxxFunctionDetail>
			<cxxFunctionDefinition>
				<cxxFunctionAccessSpecifier value="public"/>
				<cxxFunctionConst/>
				<cxxFunctionVirtual/>
				<cxxFunctionDeclaredType>IMPORT_C <apiRelation href="e32def_8h.xml#_1abb88f5378e8305d934297176fe5fa298">TInt</apiRelation>
				</cxxFunctionDeclaredType>
				<cxxFunctionScopedName>Meta::TMetaBuf8</cxxFunctionScopedName>
				<cxxFunctionPrototype>virtual IMPORT_C TInt Store(TDes8 &aBuffer) const </cxxFunctionPrototype>
				<cxxFunctionNameLookup>Meta::TMetaBuf8::Store(TDes8 &)const</cxxFunctionNameLookup>
				<cxxFunctionReimplemented href="class_meta_1_1_t_meta_var_len_base.xml#class_meta_1_1_t_meta_var_len_base_1ac91087f642fbd62f7a04a1fbc5724931">Meta::TMetaVarLenBase::Store(TDes8 &)const</cxxFunctionReimplemented>
				<cxxFunctionParameters>
					<cxxFunctionParameter>
						<cxxFunctionParameterDeclaredType>
							<apiRelation href="class_t_des8.xml#class_t_des8">TDes8</apiRelation> &</cxxFunctionParameterDeclaredType>
						<cxxFunctionParameterDeclarationName>aBuffer</cxxFunctionParameterDeclarationName>
						<apiDefNote/>
					</cxxFunctionParameter>
				</cxxFunctionParameters>
				<cxxFunctionAPIItemLocation>
					<cxxFunctionDeclarationFile name="filePath" value="K:/epoc32/include/elements/metabuffer.h"/>
					<cxxFunctionDeclarationFileLine name="lineNumber" value="30"/>
				</cxxFunctionAPIItemLocation>
			</cxxFunctionDefinition>
			<apiDesc/>
		</cxxFunctionDetail>
	</cxxFunction>
	<cxxFunction id="class_meta_1_1_t_meta_buf8_1acb1a419b02c36e55ed1f415a3b76018b">
		<apiName>Copy</apiName>
		<cxxFunctionDetail>
			<cxxFunctionDefinition>
				<cxxFunctionAccessSpecifier value="public"/>
				<cxxFunctionVirtual/>
				<cxxFunctionDeclaredType>IMPORT_C void</cxxFunctionDeclaredType>
				<cxxFunctionScopedName>Meta::TMetaBuf8</cxxFunctionScopedName>
				<cxxFunctionPrototype>virtual IMPORT_C void Copy(const TAny *aData)</cxxFunctionPrototype>
				<cxxFunctionNameLookup>Meta::TMetaBuf8::Copy(const TAny *)</cxxFunctionNameLookup>
				<cxxFunctionReimplemented href="class_meta_1_1_t_meta_var_len.xml#class_meta_1_1_t_meta_var_len_1ab00c118cb84189faf4e26cd6c4e932fd">TMetaVarLen< RBuf8 >::Copy(const TAny *)</cxxFunctionReimplemented>
				<cxxFunctionParameters>
					<cxxFunctionParameter>
						<cxxFunctionParameterDeclaredType>const <apiRelation href="e32def_8h.xml#_1af7aafba448a6eaa6ce8801f88dcb5b90">TAny</apiRelation> *</cxxFunctionParameterDeclaredType>
						<cxxFunctionParameterDeclarationName>aData</cxxFunctionParameterDeclarationName>
						<apiDefNote/>
					</cxxFunctionParameter>
				</cxxFunctionParameters>
				<cxxFunctionAPIItemLocation>
					<cxxFunctionDeclarationFile name="filePath" value="K:/epoc32/include/elements/metabuffer.h"/>
					<cxxFunctionDeclarationFileLine name="lineNumber" value="31"/>
				</cxxFunctionAPIItemLocation>
			</cxxFunctionDefinition>
			<apiDesc/>
		</cxxFunctionDetail>
	</cxxFunction>
	<cxxFunction id="class_meta_1_1_t_meta_buf8_1a3e4aa4e4e638d3be39ea32f09050a5d4">
		<apiName>Length</apiName>
		<cxxFunctionDetail>
			<cxxFunctionDefinition>
				<cxxFunctionAccessSpecifier value="public"/>
				<cxxFunctionConst/>
				<cxxFunctionVirtual/>
				<cxxFunctionDeclaredType>IMPORT_C <apiRelation href="e32def_8h.xml#_1abb88f5378e8305d934297176fe5fa298">TInt</apiRelation>
				</cxxFunctionDeclaredType>
				<cxxFunctionScopedName>Meta::TMetaBuf8</cxxFunctionScopedName>
				<cxxFunctionPrototype>virtual IMPORT_C TInt Length() const </cxxFunctionPrototype>
				<cxxFunctionNameLookup>Meta::TMetaBuf8::Length()const</cxxFunctionNameLookup>
				<cxxFunctionReimplemented href="class_meta_1_1_t_meta_var_len8.xml#class_meta_1_1_t_meta_var_len8_1a63c34b81fd818055bf9572c37b1c5e8a">TMetaVarLen8< RBuf8 >::Length()const</cxxFunctionReimplemented>
				<cxxFunctionParameters/>
				<cxxFunctionAPIItemLocation>
					<cxxFunctionDeclarationFile name="filePath" value="K:/epoc32/include/elements/metabuffer.h"/>
					<cxxFunctionDeclarationFileLine name="lineNumber" value="32"/>
				</cxxFunctionAPIItemLocation>
			</cxxFunctionDefinition>
			<apiDesc/>
		</cxxFunctionDetail>
	</cxxFunction>
	<cxxFunction id="class_meta_1_1_t_meta_buf8_1af773762bf5a18903398ad7901f968810">
		<apiName>TMetaBuf8</apiName>
		<cxxFunctionDetail>
			<cxxFunctionDefinition>
				<cxxFunctionAccessSpecifier value="protected"/>
				<cxxFunctionConstructor/>
				<cxxFunctionDeclaredType/>
				<cxxFunctionScopedName>Meta::TMetaBuf8</cxxFunctionScopedName>
				<cxxFunctionPrototype>TMetaBuf8(const TAny *aData)</cxxFunctionPrototype>
				<cxxFunctionNameLookup>Meta::TMetaBuf8::TMetaBuf8(const TAny *)</cxxFunctionNameLookup>
				<cxxFunctionParameters>
					<cxxFunctionParameter>
						<cxxFunctionParameterDeclaredType>const <apiRelation href="e32def_8h.xml#_1af7aafba448a6eaa6ce8801f88dcb5b90">TAny</apiRelation> *</cxxFunctionParameterDeclaredType>
						<cxxFunctionParameterDeclarationName>aData</cxxFunctionParameterDeclarationName>
						<apiDefNote/>
					</cxxFunctionParameter>
				</cxxFunctionParameters>
				<cxxFunctionAPIItemLocation>
					<cxxFunctionDeclarationFile name="filePath" value="K:/epoc32/include/elements/metabuffer.h"/>
					<cxxFunctionDeclarationFileLine name="lineNumber" value="35"/>
				</cxxFunctionAPIItemLocation>
			</cxxFunctionDefinition>
			<apiDesc/>
		</cxxFunctionDetail>
	</cxxFunction>
	<cxxClassInherits>
		<cxxClassInheritsDetail>
			<cxxClassFunctionInherited href="class_meta_1_1_t_meta_buf8.xml#class_meta_1_1_t_meta_var_len_base_1acfa10fa0684614ddac4ca2012f12e44a">Meta::TMetaVarLenBase::CheckBuf(TPtrC8 &)</cxxClassFunctionInherited>
			<cxxClassFunctionInherited href="class_meta_1_1_t_meta_buf8.xml#class_meta_1_1_t_meta_var_len8_1a4478b795f5a2acae7148b0ca2d19cd4d">TMetaVarLen8< RBuf8 >::AppendToBuf(TDes8 &)const</cxxClassFunctionInherited>
			<cxxClassFunctionInherited href="class_meta_1_1_t_meta_buf8.xml#class_meta_1_1_t_meta_var_len8_1a1b0af8c438239d25ebf3cea1e349e9cb">TMetaVarLen8< RBuf8 >::CopyBuf(TPtrC8 &,TInt)</cxxClassFunctionInherited>
			<cxxClassFunctionInherited href="class_meta_1_1_t_meta_buf8.xml#class_meta_1_1_t_meta_var_len8_1a8613301da55515de6853e971fe8741ea">TMetaVarLen8< RBuf8 >::TMetaVarLen8(const TAny *)</cxxClassFunctionInherited>
			<cxxClassFunctionInherited href="class_meta_1_1_t_meta_buf8.xml#class_meta_1_1_t_meta_var_len_1a49d910320dda6f21010a194bc00cce0a">TMetaVarLen< RBuf8 >::TMetaVarLen(const TAny *)</cxxClassFunctionInherited>
			<cxxClassVariableInherited href="class_meta_1_1_t_meta_buf8.xml#class_meta_1_1_t_meta_var_len_1a39f5604cc8e354bf648186ad454c47ea">TMetaVarLen< RBuf8 >::iData</cxxClassVariableInherited>
		</cxxClassInheritsDetail>
	</cxxClassInherits>
</cxxClass>