"""

   Playing around with lining up the OWL Abstract Syntax and python
   expression (class constructor) syntax.  They are pretty close.

   By sandro@w3.org, inspired in part by a conversation with
   chimezie@gmail.com on #redfoot.
   

"""

def flexargs(d, positionalArguments, args, kwargs=None):
    """

    Updates a dictionary which maps from argument names to lists of
    their values.

    Args are lined up with positionalArguments.  PositionalArguments
    are string names of the key, or -- for optional ones -- a pair of
    the string name and the default value to use of omitted.   If
    "None", then it wont be added to the dict.

    >>> values = {'a': [0]}
    >>> flexargs(values, [], [], {'a': 1, 'b':[1,2]})
    >>> values ==  {'a': [0, 1], 'b': [1, 2]}
    True

    >>> flexargs(values, ("c", "d"), ("cval", "dval"))
    >>> values == {'a': [0, 1], 'c': ['cval'], 'b': [1, 2], 'd': ['dval']}
    True

    >>> values = {'a': [0]}
    >>> flexargs(values, [("b", "b-default")], [])
    >>> values ==  {'a': [0], 'b': ['b-default']}
    True

    >>> values = {'a': [0]}
    >>> flexargs(values, [("a", "a-default")], [], {'a':1})
    >>> values ==     {'a': [0, 'a-default', 1]}
    True


    >>> values = {'a': [0]}
    >>> flexargs(values, [("a", None)], [], {'a':1})
    >>> values ==     {'a': [0, 1]}
    True


    Oh.   We need to allow skipped positional args, I think.  Use None
    for that, I guess.....   Which means you can't set a value to
    None by using position args.  I think that's okay.
    >>> values = {'a': [0]}
    >>> flexargs(values, ["a"], [None])
    >>> values == {'a': [0]}
    True


    
    """
    for i in range(0, len(positionalArguments)):
        item = positionalArguments[i]

        if isinstance(item, basestring):
            required = True
        else:
            required = False
            (item, defaultValue) = item
            
        try:
            value = args[i]
        except IndexError:
            if required:
                raise ValueError, "missing argument, position "+str(i)
            value = defaultValue
        
        if value is None: continue
        d.setdefault(item, []).append(value)
    if kwargs is not None:
        for (key, value) in kwargs.items():
            try:
                d.setdefault(key, []).extend(value)
            except TypeError:    # expect iteration over non-sequence
                d.setdefault(key, []).append(value)


class Ontology:
    """
    >>> x=Ontology("http://foo/bar", subClassOf=["cls1", "cls2"])
    >>> x.__dict__ == {'subClassOf': ['cls1', 'cls2'], 'ontologyId': ['http://foo/bar']}
    True
    
    """
    
    def __init__(self, *args, **kwargs):
        posArgs = [   ('ontologyId', None)
                  ]
        flexargs(self.__dict__, posArgs, args, kwargs)
        

class Class:

    """
    >>> x=Class('http://foo/bar', False, False)
    >>> print str(x)
    Class(<http://foo/bar> partial)

    >>> x
    Class('http://foo/bar', False, False)

    >>> x=Class('http://foo/bar', True, True)
    >>> print str(x)
    Class(<http://foo/bar> Deprecated complete)


    >>> u='http://yet.another.com/bar'
    >>> y=Class('http://foo/bar', False, False, subClassOf=[u, x, Class(None, False, False)])

    y is {'classId': ['http://foo/bar'], 'isComplete': [False], 'subClassOf': ['http://yet.another.com/bar', Class('http://foo/bar', True, True), Class(None, False, False)], 'isDeprecated': [False]}

    """
    
    def __init__(self, *args, **kwargs):
        posArgs = [   ('classId', None),     # use None for skipping?
                      ('isDeprecated', False),
                      'isComplete'
                  ]
        flexargs(self.__dict__, posArgs, args, kwargs)

    def __str__(self):
        result = "Class("
        try:
            result += "<"+self.classId[0] + "> "
        except AttributeError:
            pass
        if self.isDeprecated[0]:
            result += "Deprecated "
        if self.isComplete[0]:
            result += "complete"
        else:
            result += "partial"
        return result+")"
        

    def __repr__(self):
        result = "Class("
        try:
            result += repr(self.classId[0]) + ", "
        except AttributeError:
            result += "None, "
        result += `self.isDeprecated[0]` + ", "
        result += `self.isComplete[0]`
        return result+")"
        
        

if __name__ == "__main__":
    import doctest, sys
    doctest.testmod(sys.modules[__name__])

