<gh_stars>1-10
##
# Copyright (c) 2015-2017 Apple 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.
##

"""
A set of Python classes that wrap common CoreFoundation classes.
"""

from ._corefoundation import ffi, lib as cf


class CFError(Exception):
    """
    Generic CoreFoundation error occurred
    """


class CFObjectRef(object):
    """
    This is a wrapper for any CF objects returned from CFFI calls. For CF
    objects whose ownership is implicitly retained (CoreFoundation Create Rule),
    this class by default will CFRelease them. For an unowned object
    (CoreFoundation Get Rule), the L{owned} parameter must be set to False,
    which will cause the CF object to be explicitly CFRetain'd (and then
    automatically released).
    """

    def __init__(self, cfobj, owned=True):
        """
        Create a wrapper around the supplied cffi object that mirrors a CoreFoundation object.

        NB we have to cache the L{lib} and L{ffi} objects here to prevent them from being
        garbage collected before this object's __del__ is called.

        @param cfobj: the cffi object to wrap
        @type cfobj: L{ffi.CData}
        @param owned: whether the object is already retained
        @type owned: L{bool}
        """
        self.cf = cf
        self.ffi = ffi
        self.cfobj = cfobj if cfobj is not None else ffi.NULL
        if not owned and cfobj is not None:
            cf.CFRetain(self.cfobj)

    @classmethod
    def fromRef(cls, cfobj):
        return cls(cfobj, False)

    def __del__(self):
        """
        When this object is garbage collected, also release the CoreFoundation object, which we
        know is retained by us.
        """
        if self.cfobj != self.ffi.NULL:
            self.cf.CFRelease(self.cfobj)
        self.cfobj = None
        self.cf = None

    def ref(self):
        return self.cfobj

    def retainCount(self):
        return cf.CFGetRetainCount(self.ref())

    def instanceTypeId(self):
        return cf.CFGetTypeID(self.ref())

    def description(self):
        description = CFStringRef(cf.CFCopyDescription(self.ref()))
        return description.toString()

    def native(self):
        """
        Convert this object, if possible, into a native Python type. If it cannot be
        converted, just return it as is.
        """
        if self.instanceTypeId() == CFStringRef.typeId():
            item = CFStringRef(self.ref(), owned=False)
            result = item.native()
        elif self.instanceTypeId() == CFArrayRef.typeId():
            item = CFArrayRef(self.ref(), owned=False)
            result = item.native()
        elif self.instanceTypeId() == CFDictionaryRef.typeId():
            item = CFDictionaryRef(self.ref(), owned=False)
            result = item.native()
        else:
            result = self

        return result


class CFErrorRef(CFObjectRef):
    """
    A wrapper for CFErrorRef CoreFoundation objects.
    """

    @staticmethod
    def typeId():
        return cf.CFErrorGetTypeID()

    def code(self):
        """
        Return the error description.

        @return: error description
        @rtype: L{str}
        """
        code = cf.CFErrorGetCode(self.ref())
        return code

    def description(self):
        """
        Return the error description.

        @return: error description
        @rtype: L{str}
        """
        desc = CFStringRef(cf.CFErrorCopyDescription(self.ref()))
        return desc.toString()


class CFBooleanRef(CFObjectRef):
    """
    A wrapper for CFBooleanRef CoreFoundation objects.
    """

    @staticmethod
    def typeId():
        return cf.CFBooleanGetTypeID()

    @classmethod
    def fromBool(cls, value):
        return cls.fromRef(cf.kCFBooleanTrue if value else cf.kCFBooleanFalse)


class CFStringRef(CFObjectRef):
    """
    A wrapper for CFStringRef CoreFoundation objects.
    """

    @staticmethod
    def typeId():
        return cf.CFStringGetTypeID()

    @classmethod
    def fromString(cls, text):
        """
        Create a cffi/CFStringRef from a Python L{str} - assume UTF-8 encoding.

        @param text: utf-8 encoded string
        @type text: L{str}

        @return: the cffi data
        @rtype: L{ffi.CData}
        """
        if isinstance(text, unicode):
            text = text.encode("utf-8")
        cfstringref = cf.CFStringCreateWithCString(ffi.NULL, text, cf.kCFStringEncodingUTF8)
        if cfstringref == ffi.NULL:
            raise CFError("Unable to create a CFStringRef")
        return CFStringRef(cfstringref)

    def toString(self):
        return self.native()

    def __str__(self):
        return self.native()

    def native(self):
        """
        Convert the cffi/CFStringRef value to a utf-8 encoded Python string.

        @return: the string value
        @rtype: L{str}
        """
        # Try quick conversion first, then try longer one
        str = cf.CFStringGetCStringPtr(self.ref(), cf.kCFStringEncodingUTF8)
        if str == ffi.NULL:
            range = cf.CFStringGetLength(self.ref())
            actualSize = ffi.new("CFIndex *")
            cf.CFStringGetBytes(self.ref(), [0, range], cf.kCFStringEncodingUTF8, ord("?"), False, ffi.NULL, 0, actualSize)
            actualSize = actualSize[0]
            str = ffi.new("char []", actualSize + 1)
            cf.CFStringGetCString(self.ref(), str, actualSize + 1, cf.kCFStringEncodingUTF8)
        return ffi.string(str) if str else ""

    def copy(self):
        return CFStringRef(cf.CFStringCreateCopy(ffi.NULL, self.ref()))


class CFDataRef(CFObjectRef):
    """
    A wrapper for CFDataRef CoreFoundation objects.
    """

    @staticmethod
    def typeId():
        return cf.CFDataGetTypeID()

    @classmethod
    def fromString(cls, text):
        """
        Convert a L{str} to a cffi/CFDataRef.

        @param text: string to convert
        @type text: L{str}

        @return: the  value
        @rtype: L{CFDataRef}
        """
        cfdataref = cf.CFDataCreate(ffi.NULL, text, len(text))
        if cfdataref == ffi.NULL:
            raise CFError("Unable to create a CFDataRef")
        return CFDataRef(cfdataref)

    def toString(self):
        return self.native()

    def count(self):
        """
        The length of the data in the CFDataRef.

        @return: the count
        @rtype: L{int}
        """
        return cf.CFDataGetLength(self.ref())

    def native(self):
        """
        Convert the cffi/CFDataRef value to a Python string.

        @return: the string value
        @rtype: L{str}
        """
        count = self.count()
        value = ffi.new("UInt8 []", count)
        cf.CFDataGetBytes(self.ref(), [0, count], value)
        return bytes(ffi.buffer(value, count))


class CFArrayRef(CFObjectRef):
    """
    A wrapper for CFArrayRef CoreFoundation objects.
    """

    @staticmethod
    def typeId():
        return cf.CFArrayGetTypeID()

    @classmethod
    def fromList(cls, l):
        """
        Create a cffi/CFArrayRef from a Python L{list} of L{CFObjectRef}.

        @param l: list to convert
        @type l: L{list}

        @return: the cffi data
        @rtype: L{CFArrayRef}
        """
        lr = [item.ref() for item in l]
        cfarrayref = cf.CFArrayCreate(ffi.NULL, lr, len(l), ffi.addressof(cf.kCFTypeArrayCallBacks))
        if cfarrayref == ffi.NULL:
            raise CFError("Unable to create a CFArrayRef")
        return CFArrayRef(cfarrayref)

    def toList(self):
        return self.native()

    def count(self):
        """
        The number of items in the wrapped CFArrayRef.

        @return: the count
        @rtype: L{int}
        """
        return cf.CFArrayGetCount(self.ref())

    def valueAtIndex(self, index):
        """
        Return an item from the CFArrayRef, converted to native type.

        @param index: index of item to return
        @type index: L{int}

        @return: the cffi data
        @rtype: L{CFObjectRef}, L{str}, L{list} or L{dict}
        """
        result = cf.CFArrayGetValueAtIndex(self.ref(), index)
        if result != ffi.NULL:
            result = CFObjectRef(result, owned=False)
            return result.native()
        else:
            return None

    def native(self):
        """
        Convert a CFArrayRef into an L{list} of native items.

        @return: list of native items
        @rtype: L{list}
        """
        count = self.count()
        values = ffi.new("const void * []", count)
        cf.CFArrayGetValues(self.ref(), [0, count], values)
        items = [CFObjectRef(item, owned=False) for item in values]
        return [item.native() for item in items]


class CFDictionaryRef(CFObjectRef):
    """
    A wrapper for CFDictionaryRef CoreFoundation objects.
    """

    @staticmethod
    def typeId():
        return cf.CFDictionaryGetTypeID()

    @classmethod
    def fromDict(cls, d):
        """
        Create a cffi/CFDictionaryRef from a Python L{dict} of L{CFObjectRef}.

        @param d: dict to convert
        @type d: L{dict}

        @return: the cffi data
        @rtype: L{CFDictionaryRef}
        """
        keys = []
        values = []
        for k, v in d.items():
            keys.append(k.ref())
            values.append(v.ref())
        cfdictref = cf.CFDictionaryCreate(
            ffi.NULL, keys, values, len(keys),
            ffi.addressof(cf.kCFTypeDictionaryKeyCallBacks),
            ffi.addressof(cf.kCFTypeDictionaryValueCallBacks),
        )
        if cfdictref == ffi.NULL:
            raise CFError("Unable to create a CFDictionaryRef")
        return CFDictionaryRef(cfdictref)

    def toDict(self):
        return self.native()

    def count(self):
        """
        The number of items in the wrapped CFDictionaryRef.

        @return: the count
        @rtype: L{int}
        """
        return cf.CFDictionaryGetCount(self.ref())

    def native(self):
        """
        Convert the cffi/CFDictionaryRef value to a Python L{dict}. The keys and values
        are all converted to their native values.

        @return: the result
        @rtype: L{dict}
        """
        count = self.count()
        keys = ffi.new("const void * []", count)
        values = ffi.new("const void * []", count)
        cf.CFDictionaryGetKeysAndValues(self.ref(), keys, values)

        result = {}
        for key, value in zip(keys, values):
            k = CFStringRef(key, owned=False)
            v = CFObjectRef(value, owned=False)
            result[k.native()] = v.native()
        return result

##
# Copyright (c) 2015-2017 Apple 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.
##

"""
A set of Python classes that wrap common CoreFoundation classes.
"""

from ._corefoundation import ffi, lib as cf


class CFError(Exception):
    """
    Generic CoreFoundation error occurred
    """


class CFObjectRef(object):
    """
    This is a wrapper for any CF objects returned from CFFI calls. For CF
    objects whose ownership is implicitly retained (CoreFoundation Create Rule),
    this class by default will CFRelease them. For an unowned object
    (CoreFoundation Get Rule), the L{owned} parameter must be set to False,
    which will cause the CF object to be explicitly CFRetain'd (and then
    automatically released).
    """

    def __init__(self, cfobj, owned=True):
        """
        Create a wrapper around the supplied cffi object that mirrors a CoreFoundation object.

        NB we have to cache the L{lib} and L{ffi} objects here to prevent them from being
        garbage collected before this object's __del__ is called.

        @param cfobj: the cffi object to wrap
        @type cfobj: L{ffi.CData}
        @param owned: whether the object is already retained
        @type owned: L{bool}
        """
        self.cf = cf
        self.ffi = ffi
        self.cfobj = cfobj if cfobj is not None else ffi.NULL
        if not owned and cfobj is not None:
            cf.CFRetain(self.cfobj)

    @classmethod
    def fromRef(cls, cfobj):
        return cls(cfobj, False)

    def __del__(self):
        """
        When this object is garbage collected, also release the CoreFoundation object, which we
        know is retained by us.
        """
        if self.cfobj != self.ffi.NULL:
            self.cf.CFRelease(self.cfobj)
        self.cfobj = None
        self.cf = None

    def ref(self):
        return self.cfobj

    def retainCount(self):
        return cf.CFGetRetainCount(self.ref())

    def instanceTypeId(self):
        return cf.CFGetTypeID(self.ref())

    def description(self):
        description = CFStringRef(cf.CFCopyDescription(self.ref()))
        return description.toString()

    def native(self):
        """
        Convert this object, if possible, into a native Python type. If it cannot be
        converted, just return it as is.
        """
        if self.instanceTypeId() == CFStringRef.typeId():
            item = CFStringRef(self.ref(), owned=False)
            result = item.native()
        elif self.instanceTypeId() == CFArrayRef.typeId():
            item = CFArrayRef(self.ref(), owned=False)
            result = item.native()
        elif self.instanceTypeId() == CFDictionaryRef.typeId():
            item = CFDictionaryRef(self.ref(), owned=False)
            result = item.native()
        else:
            result = self

        return result


class CFErrorRef(CFObjectRef):
    """
    A wrapper for CFErrorRef CoreFoundation objects.
    """

    @staticmethod
    def typeId():
        return cf.CFErrorGetTypeID()

    def code(self):
        """
        Return the error description.

        @return: error description
        @rtype: L{str}
        """
        code = cf.CFErrorGetCode(self.ref())
        return code

    def description(self):
        """
        Return the error description.

        @return: error description
        @rtype: L{str}
        """
        desc = CFStringRef(cf.CFErrorCopyDescription(self.ref()))
        return desc.toString()


class CFBooleanRef(CFObjectRef):
    """
    A wrapper for CFBooleanRef CoreFoundation objects.
    """

    @staticmethod
    def typeId():
        return cf.CFBooleanGetTypeID()

    @classmethod
    def fromBool(cls, value):
        return cls.fromRef(cf.kCFBooleanTrue if value else cf.kCFBooleanFalse)


class CFStringRef(CFObjectRef):
    """
    A wrapper for CFStringRef CoreFoundation objects.
    """

    @staticmethod
    def typeId():
        return cf.CFStringGetTypeID()

    @classmethod
    def fromString(cls, text):
        """
        Create a cffi/CFStringRef from a Python L{str} - assume UTF-8 encoding.

        @param text: utf-8 encoded string
        @type text: L{str}

        @return: the cffi data
        @rtype: L{ffi.CData}
        """
        if isinstance(text, unicode):
            text = text.encode("utf-8")
        cfstringref = cf.CFStringCreateWithCString(ffi.NULL, text, cf.kCFStringEncodingUTF8)
        if cfstringref == ffi.NULL:
            raise CFError("Unable to create a CFStringRef")
        return CFStringRef(cfstringref)

    def toString(self):
        return self.native()

    def __str__(self):
        return self.native()

    def native(self):
        """
        Convert the cffi/CFStringRef value to a utf-8 encoded Python string.

        @return: the string value
        @rtype: L{str}
        """
        # Try quick conversion first, then try longer one
        str = cf.CFStringGetCStringPtr(self.ref(), cf.kCFStringEncodingUTF8)
        if str == ffi.NULL:
            range = cf.CFStringGetLength(self.ref())
            actualSize = ffi.new("CFIndex *")
            cf.CFStringGetBytes(self.ref(), [0, range], cf.kCFStringEncodingUTF8, ord("?"), False, ffi.NULL, 0, actualSize)
            actualSize = actualSize[0]
            str = ffi.new("char []", actualSize + 1)
            cf.CFStringGetCString(self.ref(), str, actualSize + 1, cf.kCFStringEncodingUTF8)
        return ffi.string(str) if str else ""

    def copy(self):
        return CFStringRef(cf.CFStringCreateCopy(ffi.NULL, self.ref()))


class CFDataRef(CFObjectRef):
    """
    A wrapper for CFDataRef CoreFoundation objects.
    """

    @staticmethod
    def typeId():
        return cf.CFDataGetTypeID()

    @classmethod
    def fromString(cls, text):
        """
        Convert a L{str} to a cffi/CFDataRef.

        @param text: string to convert
        @type text: L{str}

        @return: the  value
        @rtype: L{CFDataRef}
        """
        cfdataref = cf.CFDataCreate(ffi.NULL, text, len(text))
        if cfdataref == ffi.NULL:
            raise CFError("Unable to create a CFDataRef")
        return CFDataRef(cfdataref)

    def toString(self):
        return self.native()

    def count(self):
        """
        The length of the data in the CFDataRef.

        @return: the count
        @rtype: L{int}
        """
        return cf.CFDataGetLength(self.ref())

    def native(self):
        """
        Convert the cffi/CFDataRef value to a Python string.

        @return: the string value
        @rtype: L{str}
        """
        count = self.count()
        value = ffi.new("UInt8 []", count)
        cf.CFDataGetBytes(self.ref(), [0, count], value)
        return bytes(ffi.buffer(value, count))


class CFArrayRef(CFObjectRef):
    """
    A wrapper for CFArrayRef CoreFoundation objects.
    """

    @staticmethod
    def typeId():
        return cf.CFArrayGetTypeID()

    @classmethod
    def fromList(cls, l):
        """
        Create a cffi/CFArrayRef from a Python L{list} of L{CFObjectRef}.

        @param l: list to convert
        @type l: L{list}

        @return: the cffi data
        @rtype: L{CFArrayRef}
        """
        lr = [item.ref() for item in l]
        cfarrayref = cf.CFArrayCreate(ffi.NULL, lr, len(l), ffi.addressof(cf.kCFTypeArrayCallBacks))
        if cfarrayref == ffi.NULL:
            raise CFError("Unable to create a CFArrayRef")
        return CFArrayRef(cfarrayref)

    def toList(self):
        return self.native()

    def count(self):
        """
        The number of items in the wrapped CFArrayRef.

        @return: the count
        @rtype: L{int}
        """
        return cf.CFArrayGetCount(self.ref())

    def valueAtIndex(self, index):
        """
        Return an item from the CFArrayRef, converted to native type.

        @param index: index of item to return
        @type index: L{int}

        @return: the cffi data
        @rtype: L{CFObjectRef}, L{str}, L{list} or L{dict}
        """
        result = cf.CFArrayGetValueAtIndex(self.ref(), index)
        if result != ffi.NULL:
            result = CFObjectRef(result, owned=False)
            return result.native()
        else:
            return None

    def native(self):
        """
        Convert a CFArrayRef into an L{list} of native items.

        @return: list of native items
        @rtype: L{list}
        """
        count = self.count()
        values = ffi.new("const void * []", count)
        cf.CFArrayGetValues(self.ref(), [0, count], values)
        items = [CFObjectRef(item, owned=False) for item in values]
        return [item.native() for item in items]


class CFDictionaryRef(CFObjectRef):
    """
    A wrapper for CFDictionaryRef CoreFoundation objects.
    """

    @staticmethod
    def typeId():
        return cf.CFDictionaryGetTypeID()

    @classmethod
    def fromDict(cls, d):
        """
        Create a cffi/CFDictionaryRef from a Python L{dict} of L{CFObjectRef}.

        @param d: dict to convert
        @type d: L{dict}

        @return: the cffi data
        @rtype: L{CFDictionaryRef}
        """
        keys = []
        values = []
        for k, v in d.items():
            keys.append(k.ref())
            values.append(v.ref())
        cfdictref = cf.CFDictionaryCreate(
            ffi.NULL, keys, values, len(keys),
            ffi.addressof(cf.kCFTypeDictionaryKeyCallBacks),
            ffi.addressof(cf.kCFTypeDictionaryValueCallBacks),
        )
        if cfdictref == ffi.NULL:
            raise CFError("Unable to create a CFDictionaryRef")
        return CFDictionaryRef(cfdictref)

    def toDict(self):
        return self.native()

    def count(self):
        """
        The number of items in the wrapped CFDictionaryRef.

        @return: the count
        @rtype: L{int}
        """
        return cf.CFDictionaryGetCount(self.ref())

    def native(self):
        """
        Convert the cffi/CFDictionaryRef value to a Python L{dict}. The keys and values
        are all converted to their native values.

        @return: the result
        @rtype: L{dict}
        """
        count = self.count()
        keys = ffi.new("const void * []", count)
        values = ffi.new("const void * []", count)
        cf.CFDictionaryGetKeysAndValues(self.ref(), keys, values)

        result = {}
        for key, value in zip(keys, values):
            k = CFStringRef(key, owned=False)
            v = CFObjectRef(value, owned=False)
            result[k.native()] = v.native()
        return result
## # Copyright (c) 2015-2017 Apple 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. ## A set of Python classes that wrap common CoreFoundation classes. Generic CoreFoundation error occurred This is a wrapper for any CF objects returned from CFFI calls. For CF     objects whose ownership is implicitly retained (CoreFoundation Create Rule),     this class by default will CFRelease them. For an unowned object     (CoreFoundation Get Rule), the L{owned} parameter must be set to False,     which will cause the CF object to be explicitly CFRetain'd (and then     automatically released). Create a wrapper around the supplied cffi object that mirrors a CoreFoundation object.          NB we have to cache the L{lib} and L{ffi} objects here to prevent them from being         garbage collected before this object's __del__ is called.          @param cfobj: the cffi object to wrap         @type cfobj: L{ffi.CData}         @param owned: whether the object is already retained         @type owned: L{bool} When this object is garbage collected, also release the CoreFoundation object, which we         know is retained by us. Convert this object, if possible, into a native Python type. If it cannot be         converted, just return it as is. A wrapper for CFErrorRef CoreFoundation objects. Return the error description.          @return: error description         @rtype: L{str} Return the error description.          @return: error description         @rtype: L{str} A wrapper for CFBooleanRef CoreFoundation objects. A wrapper for CFStringRef CoreFoundation objects. Create a cffi/CFStringRef from a Python L{str} - assume UTF-8 encoding.          @param text: utf-8 encoded string         @type text: L{str}          @return: the cffi data         @rtype: L{ffi.CData} Convert the cffi/CFStringRef value to a utf-8 encoded Python string.          @return: the string value         @rtype: L{str} # Try quick conversion first, then try longer one A wrapper for CFDataRef CoreFoundation objects. Convert a L{str} to a cffi/CFDataRef.          @param text: string to convert         @type text: L{str}          @return: the  value         @rtype: L{CFDataRef} The length of the data in the CFDataRef.          @return: the count         @rtype: L{int} Convert the cffi/CFDataRef value to a Python string.          @return: the string value         @rtype: L{str} A wrapper for CFArrayRef CoreFoundation objects. Create a cffi/CFArrayRef from a Python L{list} of L{CFObjectRef}.          @param l: list to convert         @type l: L{list}          @return: the cffi data         @rtype: L{CFArrayRef} The number of items in the wrapped CFArrayRef.          @return: the count         @rtype: L{int} Return an item from the CFArrayRef, converted to native type.          @param index: index of item to return         @type index: L{int}          @return: the cffi data         @rtype: L{CFObjectRef}, L{str}, L{list} or L{dict} Convert a CFArrayRef into an L{list} of native items.          @return: list of native items         @rtype: L{list} A wrapper for CFDictionaryRef CoreFoundation objects. Create a cffi/CFDictionaryRef from a Python L{dict} of L{CFObjectRef}.          @param d: dict to convert         @type d: L{dict}          @return: the cffi data         @rtype: L{CFDictionaryRef} The number of items in the wrapped CFDictionaryRef.          @return: the count         @rtype: L{int} Convert the cffi/CFDictionaryRef value to a Python L{dict}. The keys and values         are all converted to their native values.          @return: the result         @rtype: L{dict}