<commit_before>class TreeBuilder
  include TreeKids

  attr_reader :name, :options, :tree_nodes, :bs_tree

  def self.class_for_type(type)
    raise('Obsolete tree type.') if type == :filter
    @x_tree_node_classes ||= {}
    @x_tree_node_classes[type] ||= LEFT_TREE_CLASSES[type].constantize
  end

  def initialize(name, sandbox, build = true, **_params)
    @tree_state = TreeState.new(sandbox)
    @sb = sandbox # FIXME: some subclasses still access @sb

    @locals_for_render  = {}
    @name               = name.to_sym # includes _tree
    @options            = tree_init_options
    @tree_nodes         = []

    add_to_sandbox
    build_tree if build
  end

  def node_by_tree_id(id)
    model, rec_id, prefix = self.class.extract_node_model_and_id(id)

    case model
    when 'Hash' # create a fake hash node
      {:type => prefix, :id => rec_id, :full_id => id}
    when nil # no model, probably super() called from a redefinition
      nil
    else
      model.constantize.find(rec_id)
    end
  end

  # Get the children of a tree node that is being expanded (autoloaded)
  def x_get_child_nodes(id)
    parents = [] # FIXME: parent ids should be provided on autoload as well

    object = node_by_tree_id(id)

    # Save node as open
    open_node(id)

    x_get_tree_objects(object, @tree_state.x_tree(@name), false, parents).map do |o|
      x_build_node_tree(o, id, @tree_state.x_tree(@name))
    end
  end

  # The possible options are
  # * full_ids - whether to generate full node IDs or not
  # * open_all - expand all expandable nodes
  # * lazy - is the tree lazily-loadable
  # * checkboxes - show checkboxes for the nodes
  # * allow_reselect - fire the onclick event if a selected node is reselected
  # * three_checks - hierarchically check the parent if all children are checked
  # * post_check - some kind of post-processing hierarchical checks
  # * silent_activate - whether to activate the active_node silently or not (by default for explorers)
  def tree_init_options
    $log.warn("MIQ(#{self.class.name}) - TreeBuilder descendants should have their own tree_init_options")
    {}
  end

  # Get nodes model (folder, Vm, Cluster, etc)
  def self.get_model_for_prefix(node_prefix)
    X_TREE_NODE_PREFIXES[node_prefix]
  end

  def self.get_prefix_for_model(model)
    model = model.to_s unless model.kind_of?(String)
    X_TREE_NODE_PREFIXES_INVERTED[model]
  end

  def self.build_node_id(record)
    prefix = get_prefix_for_model(record.class.base_model)
    "#{prefix}-#{record.id}"
  end

  # return this nodes model and record id
  def self.extract_node_model_and_id(node_id)
    prefix, record_id = node_id.split("_").last.split('-')
    model = get_model_for_prefix(prefix)
    [model, record_id, prefix]
  end

  def locals_for_render
    @locals_for_render.update(:select_node => @tree_state.x_node(@name).to_s)
  end

  def reload!
    build_tree
  end

  def open_node(id)
    open_nodes = @tree_state.x_tree(@name)[:open_nodes]
    open_nodes.push(id) unless open_nodes.include?(id)
  end

  # FIXME: temporary conversion, needs to be moved into the generation
  def self.convert_bs_tree(nodes)
    return [] if nodes.nil?
    nodes = [nodes] if nodes.kind_of?(Hash)
    stack = nodes.dup
    while stack.any?
      node = stack.pop
      stack += node[:children] if node.key?(:children)
      stack += node[:nodes] if node.key?(:nodes)
      node[:text] = node.delete(:title) if node.key?(:title)
      node[:nodes] = node.delete(:children) if node.key?(:children)
      node[:lazyLoad] = node.delete(:isLazy) if node.key?(:isLazy)
      node[:state] = {}
      node[:state][:expanded] = node.delete(:expand) if node.key?(:expand)
      node[:state][:checked] = node.delete(:select) if node.key?(:select)
      node[:state][:selected] = node.delete(:highlighted) if node.key?(:highlighted)
      node[:selectable] = !node.delete(:cfmeNoClick) if node.key?(:cfmeNoClick)
      node[:class] = ''
      node[:class] = node.delete(:addClass) if node.key?(:addClass) && !node[:addClass].nil?
      node[:class] = node[:class].split(' ').push('no-cursor').join(' ') if node[:selectable] == false
    end
    nodes<commit_msg>Drop the conversion of legacy options from TreeBuilder#convert_bs_tree<commit_after>class TreeBuilder
  include TreeKids

  attr_reader :name, :options, :tree_nodes, :bs_tree

  def self.class_for_type(type)
    raise('Obsolete tree type.') if type == :filter
    @x_tree_node_classes ||= {}
    @x_tree_node_classes[type] ||= LEFT_TREE_CLASSES[type].constantize
  end

  def initialize(name, sandbox, build = true, **_params)
    @tree_state = TreeState.new(sandbox)
    @sb = sandbox # FIXME: some subclasses still access @sb

    @locals_for_render  = {}
    @name               = name.to_sym # includes _tree
    @options            = tree_init_options
    @tree_nodes         = []

    add_to_sandbox
    build_tree if build
  end

  def node_by_tree_id(id)
    model, rec_id, prefix = self.class.extract_node_model_and_id(id)

    case model
    when 'Hash' # create a fake hash node
      {:type => prefix, :id => rec_id, :full_id => id}
    when nil # no model, probably super() called from a redefinition
      nil
    else
      model.constantize.find(rec_id)
    end
  end

  # Get the children of a tree node that is being expanded (autoloaded)
  def x_get_child_nodes(id)
    parents = [] # FIXME: parent ids should be provided on autoload as well

    object = node_by_tree_id(id)

    # Save node as open
    open_node(id)

    x_get_tree_objects(object, @tree_state.x_tree(@name), false, parents).map do |o|
      x_build_node_tree(o, id, @tree_state.x_tree(@name))
    end
  end

  # The possible options are
  # * full_ids - whether to generate full node IDs or not
  # * open_all - expand all expandable nodes
  # * lazy - is the tree lazily-loadable
  # * checkboxes - show checkboxes for the nodes
  # * allow_reselect - fire the onclick event if a selected node is reselected
  # * three_checks - hierarchically check the parent if all children are checked
  # * post_check - some kind of post-processing hierarchical checks
  # * silent_activate - whether to activate the active_node silently or not (by default for explorers)
  def tree_init_options
    $log.warn("MIQ(#{self.class.name}) - TreeBuilder descendants should have their own tree_init_options")
    {}
  end

  # Get nodes model (folder, Vm, Cluster, etc)
  def self.get_model_for_prefix(node_prefix)
    X_TREE_NODE_PREFIXES[node_prefix]
  end

  def self.get_prefix_for_model(model)
    model = model.to_s unless model.kind_of?(String)
    X_TREE_NODE_PREFIXES_INVERTED[model]
  end

  def self.build_node_id(record)
    prefix = get_prefix_for_model(record.class.base_model)
    "#{prefix}-#{record.id}"
  end

  # return this nodes model and record id
  def self.extract_node_model_and_id(node_id)
    prefix, record_id = node_id.split("_").last.split('-')
    model = get_model_for_prefix(prefix)
    [model, record_id, prefix]
  end

  def locals_for_render
    @locals_for_render.update(:select_node => @tree_state.x_node(@name).to_s)
  end

  def reload!
    build_tree
  end

  def open_node(id)
    open_nodes = @tree_state.x_tree(@name)[:open_nodes]
    open_nodes.push(id) unless open_nodes.include?(id)
  end

  # FIXME: temporary conversion, needs to be moved into the generation
  def self.convert_bs_tree(nodes)
    return [] if nodes.nil?
    nodes = [nodes] if nodes.kind_of?(Hash)
    stack = nodes.dup
    while stack.any?
      node = stack.pop
      stack += node[:nodes] if node.key?(:nodes)
      node[:lazyLoad] = node.delete(:isLazy) if node.key?(:isLazy)
      node[:state] = {}
      node[:state][:expanded] = node.delete(:expand) if node.key?(:expand)
      node[:state][:checked] = node.delete(:select) if node.key?(:select)
      node[:state][:selected] = node.delete(:highlighted) if node.key?(:highlighted)
      node[:class] = ''
      node[:class] = node.delete(:addClass) if node.key?(:addClass) && !node[:addClass].nil?
      node[:class] = node[:class].split(' ').push('no-cursor').join(' ') if node[:selectable] == false
    end
    nodes
{'fraction_non_alphanumeric': 0.11348825771548014, 'fraction_numerical': 0.0, 'mean_word_length': 3.531476323119777, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '336961', 'n_tokens_mistral': 2742, 'n_tokens_neox': 2691, 'n_words': 682}