text: # Copyright 2013 Red Hat, Inc.
  # All Rights Reserved.
  #
  #    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 oslo_serialization import jsonutils
  from oslo_utils import strutils
  import six
  import wsme
  from wsme import types as wtypes
  
  from watcher._i18n import _
  from watcher.common import exception
  from watcher.common import utils
  
  
  class UuidOrNameType(wtypes.UserType):
      """A simple UUID or logical name type."""
  
      basetype = wtypes.text
      name = 'uuid_or_name'
  
      @staticmethod
      def validate(value):
          if not (utils.is_uuid_like(value) or utils.is_hostname_safe(value)):
              raise exception.InvalidUuidOrName(name=value)
          return value
  
      @staticmethod
      def frombasetype(value):
          if value is None:
              return None
          return UuidOrNameType.validate(value)
  
  
  class NameType(wtypes.UserType):
      """A simple logical name type."""
  
      basetype = wtypes.text
      name = 'name'
  
      @staticmethod
      def validate(value):
          if not utils.is_hostname_safe(value):
              raise exception.InvalidName(name=value)
          return value
  
      @staticmethod
      def frombasetype(value):
          if value is None:
              return None
          return NameType.validate(value)
  
  
  class UuidType(wtypes.UserType):
      """A simple UUID type."""
  
      basetype = wtypes.text
      name = 'uuid'
  
      @staticmethod
      def validate(value):
          if not utils.is_uuid_like(value):
              raise exception.InvalidUUID(uuid=value)
          return value
  
      @staticmethod
      def frombasetype(value):
          if value is None:
              return None
          return UuidType.validate(value)
  
  
  class BooleanType(wtypes.UserType):
      """A simple boolean type."""
  
      basetype = wtypes.text
      name = 'boolean'
  
      @staticmethod
      def validate(value):
          try:
              return strutils.bool_from_string(value, strict=True)
          except ValueError as e:
              # raise Invalid to return 400 (BadRequest) in the API
              raise exception.Invalid(e)
  
      @staticmethod
      def frombasetype(value):
          if value is None:
              return None
          return BooleanType.validate(value)
  
  
  class JsonType(wtypes.UserType):
      """A simple JSON type."""
  
      basetype = wtypes.text
      name = 'json'
  
      def __str__(self):
          # These are the json serializable native types
          return ' | '.join(map(str, (wtypes.text, six.integer_types, float,
                                      BooleanType, list, dict, None)))
  
      @staticmethod
      def validate(value):
          try:
              jsonutils.dumps(value, default=None)
          except TypeError:
              raise exception.Invalid(_('%s is not JSON serializable') % value)
          else:
              return value
  
      @staticmethod
      def frombasetype(value):
          return JsonType.validate(value)
  
  
  uuid = UuidType()
  boolean = BooleanType()
  jsontype = JsonType()
  
  
  class MultiType(wtypes.UserType):
      """A complex type that represents one or more types.
  
      Used for validating that a value is an instance of one of the types.
  
      :param types: Variable-length list of types.
  
      """
      def __init__(self, *types):
          self.types = types
  
      def __str__(self):
          return ' | '.join(map(str, self.types))
  
      def validate(self, value):
          for t in self.types:
              if t is wsme.types.text and isinstance(value, wsme.types.bytes):
                  value = value.decode()
              if isinstance(value, t):
                  return value
          else:
              raise ValueError(
                  _("Wrong type. Expected '%(type)s', got '%(value)s'"),
                  type=self.types, value=type(value)
              )
  
  
  class JsonPatchType(wtypes.Base):
      """A complex type that represents a single json-patch operation."""
  
      path = wtypes.wsattr(wtypes.StringType(pattern='^(/[\w-]+)+$'),
                           mandatory=True)
      op = wtypes.wsattr(wtypes.Enum(str, 'add', 'replace', 'remove'),
                         mandatory=True)
      value = wsme.wsattr(jsontype, default=wtypes.Unset)
  
      @staticmethod
      def internal_attrs():
          """Returns a list of internal attributes.
  
          Internal attributes can't be added, replaced or removed. This
          method may be overwritten by derived class.
  
          """
          return ['/created_at', '/id', '/links', '/updated_at',
                  '/deleted_at', '/uuid']
  
      @staticmethod
      def mandatory_attrs():
          """Returns a list of mandatory attributes.
  
          Mandatory attributes can't be removed from the document. This
          method should be overwritten by derived class.
  
          """
          return []
  
      @staticmethod
      def validate(patch):
          _path = '/{0}'.format(patch.path.split('/')[1])
          if _path in patch.internal_attrs():
              msg = _("'%s' is an internal attribute and can not be updated")
              raise wsme.exc.ClientSideError(msg % patch.path)
  
          if patch.path in patch.mandatory_attrs() and patch.op == 'remove':
              msg = _("'%s' is a mandatory attribute and can not be removed")
              raise wsme.exc.ClientSideError(msg % patch.path)
  
          if patch.op != 'remove':
              if patch.value is wsme.Unset:
                  msg = _("'add' and 'replace' operations needs value")
                  raise wsme.exc.ClientSideError(msg)
  
          ret = {'path': patch.path, 'op': patch.op}
          if patch.value is not wsme.Unset:
              ret['value'] = patch.value
          return ret
  