text: # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  #
  # http://www.apache.org/licenses/LICENSE-2.0
  #
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  
  from ...utils.console import puts, Colored, indent
  
  
  # We are inheriting the primitive types in order to add the ability to set
  # an attribute (_locator) on them.
  
  class LocatableString(unicode):
      pass
  
  
  class LocatableInt(int):
      pass
  
  
  class LocatableFloat(float):
      pass
  
  
  def wrap(value):
      if isinstance(value, basestring):
          return True, LocatableString(value)
      elif isinstance(value, int) and \
              not isinstance(value, bool):  # Note: bool counts as int in Python!
          return True, LocatableInt(value)
      elif isinstance(value, float):
          return True, LocatableFloat(value)
      return False, value
  
  
  class Locator(object):
      """
      Stores location information (line and column numbers) for agnostic raw data.
      """
      def __init__(self, location, line, column, children=None):
          self.location = location
          self.line = line
          self.column = column
          self.children = children
  
      def get_child(self, *names):
          if (not names) or (not isinstance(self.children, dict)):
              return self
          name = names[0]
          if name not in self.children:
              return self
          child = self.children[name]
          return child.get_child(names[1:])
  
      def link(self, raw, path=None):
          if hasattr(raw, '_locator'):
              # This can happen when we use anchors
              return
  
          try:
              setattr(raw, '_locator', self)
          except AttributeError:
              return
  
          if isinstance(raw, list):
              for i, raw_element in enumerate(raw):
                  wrapped, raw_element = wrap(raw_element)
                  if wrapped:
                      raw[i] = raw_element
                  child_path = '%s.%d' % (path, i) if path else str(i)
                  try:
                      self.children[i].link(raw_element, child_path)
                  except KeyError:
                      raise ValueError('location map does not match agnostic raw data: %s' %
                                       child_path)
          elif isinstance(raw, dict):
              for k, raw_element in raw.iteritems():
                  wrapped, raw_element = wrap(raw_element)
                  if wrapped:
                      raw[k] = raw_element
                  child_path = '%s.%s' % (path, k) if path else k
                  try:
                      self.children[k].link(raw_element, child_path)
                  except KeyError:
                      raise ValueError('location map does not match agnostic raw data: %s' %
                                       child_path)
  
      def merge(self, locator):
          if isinstance(self.children, dict) and isinstance(locator.children, dict):
              for k, loc in locator.children.iteritems():
                  if k in self.children:
                      self.children[k].merge(loc)
                  else:
                      self.children[k] = loc
  
      def dump(self, key=None):
          if key:
              puts('%s "%s":%d:%d' %
                   (Colored.red(key), Colored.blue(self.location), self.line, self.column))
          else:
              puts('"%s":%d:%d' % (Colored.blue(self.location), self.line, self.column))
          if isinstance(self.children, list):
              with indent(2):
                  for loc in self.children:
                      loc.dump()
          elif isinstance(self.children, dict):
              with indent(2):
                  for k, loc in self.children.iteritems():
                      loc.dump(k)
  
      def __str__(self):
          # Should be in same format as Issue.locator_as_str
          return '"%s":%d:%d' % (self.location, self.line, self.column)
  