01
  #   9li~h
n˪bѿeB ?     9# Copyright (C) 2011-2016 Red Hat, Inc.
# (C) Copyright 2016-2017 Hewlett Packard Enterprise Development LP
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; If not, see <http://www.gnu.org/licenses/>.
#
# Author: tasleson
#         Gris Ge <fge@redhat.com>
#         Joe Handzik <joseph.t.handzik@hpe.com>

from abc import ABCMeta as _ABCMeta
import re
import binascii
from six import with_metaclass

try:
    import simplejson as json
except ImportError:
    import json

from json.decoder import WHITESPACE

from lsm._common import get_class, default_property, ErrorNumber, LsmError

import six


class DataEncoder(json.JSONEncoder):
    """
    Custom json encoder for objects derived form ILsmData
    """

    def default(self, my_class):
        if not isinstance(my_class, IData):
            raise ValueError('incorrect class type:' + str(type(my_class)))
        else:
            return my_class._to_dict()


class DataDecoder(json.JSONDecoder):
    """
    Custom json decoder for objects derived from ILsmData
    """

    @staticmethod
    def __process_dict(d):
        """
        Processes a dictionary
        """
        rc = {}

        if 'class' in d:
            rc = IData._factory(d)
        else:
            for (k, v) in d.items():
                rc[k] = DataDecoder.__decode(v)

        return rc

    @staticmethod
    def __process_list(l):
        """
        Processes a list
        """
        rc = []
        for elem, value in enumerate(l):
            if type(value) is list:
                rc.append(DataDecoder.__process_list(value))
            elif type(value) is dict:
                rc.append(DataDecoder.__process_dict(value))
            else:
                rc.append(value)
        return rc

    @staticmethod
    def __decode(e):
        """
        Decodes the parsed json
        """
        if type(e) is dict:
            return DataDecoder.__process_dict(e)
        elif type(e) is list:
            return DataDecoder.__process_list(e)
        else:
            return e

    def decode(self, json_string, _w=WHITESPACE.match):
        return DataDecoder.__decode(json.loads(json_string))


class IData(with_metaclass(_ABCMeta, object)):
    """
    Base class functionality of serializable
    classes.
    """

    def _to_dict(self):
        """
        Represent the class as a dictionary
        """
        rc = {'class': self.__class__.__name__}

        # If one of the attributes is another IData we will
        # process that too, is there a better way to handle this?
        for (k, v) in list(self.__dict__.items()):
            if isinstance(v, IData):
                rc[k[1:]] = v._to_dict()
            else:
                rc[k[1:]] = v

        return rc

    @staticmethod
    def _factory(d):
        """
        Factory for creating the appropriate class given a dictionary.
        This only works for objects that inherit from IData
        """
        if 'class' in d:
            class_name = d['class']
            del d['class']
            c = get_class(__name__ + '.' + class_name)

            # If any of the parameters are themselves an IData process them
            for k, v in list(d.items()):
                if isinstance(v, dict) and 'class' in v:
                    d['_' + k] = IData._factory(d.pop(k))
                else:
                    d['_' + k] = d.pop(k)

            return c(**d)

    def __str__(self):
        """
        Used for human string representation.
        """
   