class ActiveLdap::Schema

def alias_map(group)

def alias_map(group)
  ensure_parse(group)
  return {} if @schema_info[group].nil?
  @schema_info[group][:aliases] || {}
end

def attribute(name)

def attribute(name)
  name = name.to_s if name.is_a?(Symbol)
  cache([:attribute, name]) do
    Attribute.new(name, self)
  end
end

def attribute_type(name, attribute_name)

def attribute_type(name, attribute_name)
  cache([:attribute_type, name, attribute_name]) do
    fetch("attributeTypes", name, attribute_name)
  end
end

def attributes

def attributes
  cache([:attributes]) do
    names("attributeTypes").collect do |name|
      attribute(name)
    end
  end
end

def cache(key)

def cache(key)
  (@cache[key] ||= [yield])[0]
end

def default_entries

def default_entries
  {
    "objectClasses" => [],
    "attributeTypes" => [],
    "ldapSyntaxes" => [],
    "dITContentRules" => [],
    "matchingRules" => [],
  }
end

def determine_id_or_name(id_or_name, aliases)

def determine_id_or_name(id_or_name, aliases)
  if /\A[\d\.]+\z/ =~ id_or_name
    id = id_or_name
    name = nil
  else
    name = normalize_schema_name(id_or_name)
    id = aliases[name]
  end
  [id, name]
end

def dit_content_rule_attribute(name, attribute_name)

def dit_content_rule_attribute(name, attribute_name)
  cache([:dit_content_rule_attribute, name, attribute_name]) do
    fetch("dITContentRules", name, attribute_name)
  end
end

def dump(output=nil)

def dump(output=nil)
  require 'pp'
  output ||= STDOUT
  if output.respond_to?(:write)
    PP.pp(@entries, output)
  else
    open(output, "w") {|out| PP.pp(@entries, out)}
  end
  nil
end

def ensure_parse(group)

def ensure_parse(group)
  return if @entries[group].nil?
  unless @entries[group].empty?
    fetch(group, 'nonexistent', 'nonexistent')
  end
end

def ensure_schema_info(group)

def ensure_schema_info(group)
  @schema_info[group] ||= {:ids => {}, :aliases => {}}
  info = @schema_info[group]
  [info, info[:ids], info[:aliases]]
end

def entry(group, id_or_name)

def entry(group, id_or_name)
  return {} if group.empty? or id_or_name.empty?
  unless @entries.has_key?(group)
    raise ArgumentError, _("Unknown schema group: %s") % group
  end
  # Initialize anything that is required
  info, ids, aliases = ensure_schema_info(group)
  _ = info # for suppress a warning on Ruby 1.9.3
  id, name = determine_id_or_name(id_or_name, aliases)
  # Check already parsed options first
  return ids[id] if ids.has_key?(id)
  schemata = @entries[group] || []
  while schema = schemata.shift
    next unless /\A\s*\(\s*(#{OID_RE})\s*(.*)\s*\)\s*\z/ =~ schema
    schema_id = $1
    rest = $2
    if ids.has_key?(schema_id)
      attributes = ids[schema_id]
    else
      attributes = {}
      ids[schema_id] = attributes
    end
    parse_attributes(rest, attributes)
    (attributes["NAME"] || []).each do |v|
      normalized_name = normalize_schema_name(v)
      aliases[normalized_name] = schema_id
      id = schema_id if id.nil? and name == normalized_name
    end
    break if id == schema_id
  end
  ids[id || aliases[name]] || {}
end

def exist_name?(group, name)

def exist_name?(group, name)
  alias_map(group).has_key?(normalize_schema_name(name))
end

def fetch(group, id_or_name, attribute_name)

fetch('ldapSyntaxes', '1.3.6.1.4.1.1466.115.121.1.5', 'DESC')
fetch('attributeTypes', 'cn', 'DESC')
e.g.
look up in any of the given keys.
This is just like LDAP::Schema#attribute except that it allows

fetch
def fetch(group, id_or_name, attribute_name)
  return [] if attribute_name.empty?
  attribute_name = normalize_attribute_name(attribute_name)
  value = entry(group, id_or_name)[attribute_name]
  value ? value.dup : []
end

def ids(group)

def ids(group)
  ensure_parse(group)
  info, ids, aliases = ensure_schema_info(group)
  _ = info = aliases # for suppress a warning on Ruby 1.9.3
  ids.keys
end

def initialize(entries)

def initialize(entries)
  @entries = normalize_entries(entries || {})
  @schema_info = {}
  @class_attributes_info = {}
  @cache = {}
end

def ldap_syntax(name)

def ldap_syntax(name)
  cache([:ldap_syntax, name]) do
    Syntax.new(name, self)
  end
end

def ldap_syntax_attribute(name, attribute_name)

def ldap_syntax_attribute(name, attribute_name)
  cache([:ldap_syntax_attribute, name, attribute_name]) do
    fetch("ldapSyntaxes", name, attribute_name)
  end
end

def ldap_syntaxes

def ldap_syntaxes
  cache([:ldap_syntaxes]) do
    ids("ldapSyntaxes").collect do |id|
      ldap_syntax(id)
    end
  end
end

def names(group)

def names(group)
  alias_map(group).keys
end

def normalize_attribute_name(name)

def normalize_attribute_name(name)
  name.upcase.gsub(/_/, "-")
end

def normalize_entries(entries)

def normalize_entries(entries)
  normalized_entries = default_entries
  normalized_keys = normalized_entries.keys
  entries.each do |name, values|
    normalized_name = normalized_keys.find do |key|
      key.downcase == name
    end
    normalized_entries[normalized_name || name] = values
  end
  normalized_entries
end

def normalize_schema_name(name)

def normalize_schema_name(name)
  name.downcase.sub(/;.*$/, '')
end

def object_class(name)

def object_class(name)
  cache([:object_class, name]) do
    ObjectClass.new(name, self)
  end
end

def object_class_attribute(name, attribute_name)

def object_class_attribute(name, attribute_name)
  cache([:object_class_attribute, name, attribute_name]) do
    fetch("objectClasses", name, attribute_name)
  end
end

def object_classes

def object_classes
  cache([:object_classes]) do
    names("objectClasses").collect do |name|
      object_class(name)
    end
  end
end

def parse_attributes(str, attributes)

def parse_attributes(str, attributes)
  str.scan(/([A-Z\-_]+)\s+
            (?:\(\s*(\w[\w\-;]*(?:\s+\$\s+\w[\w\-;]*)*)\s*\)|
               \(\s*([^\)]*)\s*\)|
               '([^\']*)'|
               ((?!#{RESERVED_NAMES_RE})[a-zA-Z][a-zA-Z\d\-;]*)|
               (\d[\d\.\{\}]+)|
               ()
            )/x
           ) do |name, multi_amp, multi, string, literal, syntax, no_value|
    case
    when multi_amp
      values = multi_amp.rstrip.split(/\s*\$\s*/)
    when multi
      values = multi.scan(/\s*'([^\']*)'\s*/).collect {|value| value[0]}
    when string
      values = [string]
    when literal
      values = [literal]
    when syntax
      values = [syntax]
    when no_value
      values = ["TRUE"]
    end
    attributes[normalize_attribute_name(name)] ||= []
    attributes[normalize_attribute_name(name)].concat(values)
  end
end

def resolve_name(group, name)

def resolve_name(group, name)
  alias_map(group)[normalize_schema_name(name)]
end