text: # -*- coding: utf-8 -*-
  
  from __future__ import absolute_import
  import json
  import os
  import logging
  import re
  
  import six
  import yaml
  
  from .. import consts
  from ..utils import patch_path
  
  logger = logging.getLogger(__name__)
  
  
  class Getter(six.Iterator):
      """ base of getter object
  
      Idealy, to subclass a getter, you just need to override load function.
      The part to extend getter would be finalized once Swagger 2.0 is ready.
      """
  
      def __init__(self, path):
          self.base_path = path
  
      def __iter__(self):
          return self
  
      def __next__(self):
          if not self.urls:
              raise StopIteration
  
          obj = self.load(self.urls.pop(0))
  
          # make sure data is string type
          if isinstance(obj, dict):
              pass
          elif isinstance(obj, six.binary_type):
              obj = obj.decode('utf-8')
          elif not isinstance(obj, six.string_types):
              raise ValueError('Unknown types: [{0}]'.format(str(type(obj))))
  
          # a very simple logic to distinguish json and yaml
          if isinstance(obj, six.string_types):
              try:
                  if obj.startswith('{'):
                      obj = json.loads(obj)
                  else:
                      obj = yaml.load(obj)
              except ValueError:
                  raise Exception('Unknown format startswith {0} ...'.format(
                      obj[:10]))
  
          return obj
  
      def load(self, path):
          """ load the resource, and return for parsing.
  
          :return: name and json object of resources
          :rtype: (str, dict)
          """
          raise NotImplementedError()
  
  
  class LocalGetter(Getter):
      """ default getter implmenetation for local resource file
      """
  
      def __init__(self, path):
          super(LocalGetter, self).__init__(path)
  
          if path.startswith('file://'):
              parsed = six.moves.urllib.parse.urlparse(path)
              path = parsed.path
          if re.match('^/[A-Z]+:', path) is not None:
              path = os.path.abspath(path[1:])
  
          for name in consts.SWAGGER_FILE_NAMES:
              if self.base_path.endswith(name):
                  self.base_path = os.path.dirname(self.base_path)
                  self.urls = [path]
                  return
              else:
                  target_path = os.path.join(path, name)
                  if os.path.isfile(target_path):
                      self.urls = [target_path]
                      return
  
          # there is no file matched predefined file name:
          # - resource_list.json (1.2)
          # - swagger.json       (2.0)
          # in this case, we will locate them in this way:
          # - when 'path' points to a specific file, and its
          #   extension is either 'json' or 'yaml'.
          _, ext = os.path.splitext(path)
          for e in [
                  consts.FILE_EXT_JSON, consts.FILE_EXT_YAML, consts.FILE_EXT_YML
          ]:
              if ext.endswith(e):
                  self.base_path = os.path.dirname(path)
                  self.urls = [path]
                  return
  
          for e in [consts.FILE_EXT_JSON, consts.FILE_EXT_YAML]:
              if os.path.isfile(path + '.' + e):
                  self.urls = [path + '.' + e]
                  break
          else:
              raise ValueError(
                  'Unable to locate resource file: [{}]'.format(path))
  
      def load(self, path):
          logger.info('to load: [%s]', path)
  
          path = patch_path(self.base_path, path)
          logger.info('final path to load: [%s]', path)
  
          ret = None
          with open(path, 'r') as file_handle:
              ret = file_handle.read()
          return ret
  
  
  class SimpleGetter(Getter):
      """ the simple getter that don't have to concern file loading of LocalGetter
      """
  
      __simple_getter_callback__ = lambda url: {}
      """ the callback to load the resource, accept an URL and return a string buffer
      """
  
      def __init__(self, path):
          if isinstance(path, six.string_types):
              super(SimpleGetter, self).__init__(path)
              if self.base_path.endswith('/'):
                  self.base_path = self.base_path[:-1]
              self.urls = [path]
          else:
              raise Exception(
                  'Unsupported type for "path": {} in SimpleGetter'.format(
                      str(type(path))))
  
      def load(self, path):
          logger.info('to load: [%s]', path)
  
          return self.__simple_getter_callback__.__func__(path)
  
  
  def _url_load(path):
      ret = file_handle = None
      try:
          file_handle = six.moves.urllib.request.urlopen(path)
          ret = file_handle.read()
      finally:
          if file_handle:
              file_handle.close()
  
      return ret
  
  
  class UrlGetter(SimpleGetter):
      """ default getter implementation for remote resource file
      """
      __simple_getter_callback__ = _url_load
  
  
  class DictGetter(Getter):
      """ a getter accept a dict as parameter without loading from file / url
  
      args:
       - urls: the urls to be loaded in upcoming resolving (the order should be matched to get result correct)
       - path2dict: a mapping from 'path' to 'dict', which is the mocking of 'downloaded data'
      """
  
      def __init__(self, urls, path2dict):
          super(DictGetter, self).__init__(urls[0])
          self.urls = urls
          self._path2dict = path2dict or {}
  
      def load(self, path):
          logger.info('to load: [%s]', path)
  
          return self._path2dict.get(path, {})
  