Adding support for default values
authorlbessard
Fri, 31 Aug 2007 15:09:05 +0200
changeset 83 180e4a0160b2
parent 82 119b62c73085
child 84 0369ad49e67f
Adding support for default values
xmlclass/xmlclass.py
--- a/xmlclass/xmlclass.py	Fri Aug 31 15:08:11 2007 +0200
+++ b/xmlclass/xmlclass.py	Fri Aug 31 15:09:05 2007 +0200
@@ -88,8 +88,10 @@
              return False
          else:
             raise ValueError, "\"%s\" is not a valid boolean!"%value
-    elif type_compute in ["decimal","unsignedLong","long","integer"]:
+    elif type_compute in ["unsignedLong","long","integer"]:
         return int(value)
+    elif type_compute == "decimal":
+        return float(value)
     elif type_compute in ["string","anyURI","NMTOKEN","language"]:
         return value
     elif type_compute == "time":
@@ -123,6 +125,11 @@
         print "Can't affect: %s"%type_compute
         return None
 
+def GetInitialValueFunction(value):
+    def GetInitialValue():
+        return value
+    return GetInitialValue
+
 """
 Class that generate class from an XML Tree 
 """
@@ -158,6 +165,7 @@
                 # This tags defines an attribute of the class
                 if name in ["element", "attribute"]:
                     nodename = GetAttributeValue(node._attrs["name"])
+                    default = None
                     if "type" in node._attrs:
                         nodetype = GetAttributeValue(node._attrs["type"])
                         if nodetype.startswith("xsd"):
@@ -179,7 +187,9 @@
                             use = GetAttributeValue(node._attrs["use"])
                         else:
                             use = "optional"
-                    if name == "element":
+                        if "default" in node._attrs:
+                            default = GetAttributeValue(node._attrs["default"])
+                    elif name == "element":
                         # If a tag can be written more than one time we define a list attribute
                         if "maxOccurs" in node._attrs and GetAttributeValue(node._attrs["maxOccurs"]) == "unbounded":
                             nodetype = "%s[]"%nodetype
@@ -187,7 +197,7 @@
                             use = "optional"
                         else:
                             use = "required"
-                    attributes[nodename] = (nodetype, name, use)
+                    attributes[nodename] = (nodetype, name, use, default)
                     if sequence:
                         order.append(nodename)
                 
@@ -214,7 +224,7 @@
                         choices = attrs
                     else:
                         choices = {}
-                        for attr, (attr_type, xml_type, write_type) in attrs.items():
+                        for attr, (attr_type, xml_type, write_type, default) in attrs.items():
                             choices[attr] = attr_type
                     if "maxOccurs" in node._attrs and GetAttributeValue(node._attrs["maxOccurs"]) == "unbounded":
                         attributes["multichoice_content"] = choices
@@ -230,11 +240,11 @@
                 elif name in "sequence":
                     super, attrs, order = self.GenerateXSDClasses(node, parent, True)
                     if "maxOccurs" in node._attrs and GetAttributeValue(node._attrs["maxOccurs"]) == "unbounded":
-                        for attr, (attr_type, xml_type, write_type) in attrs.items():
-                            attrs[attr] = ("%s[]"%attr_type, xml_type, write_type)
+                        for attr, (attr_type, xml_type, write_type, default) in attrs.items():
+                            attrs[attr] = ("%s[]"%attr_type, xml_type, write_type, default)
                     if "minOccurs" in node._attrs and GetAttributeValue(node._attrs["minOccurs"]) == "0":
-                        for attr, (attr_type, xml_type, write_type) in attrs.items():
-                            attrs[attr] = (attr_type, xml_type, "optional")
+                        for attr, (attr_type, xml_type, write_type, default) in attrs.items():
+                            attrs[attr] = (attr_type, xml_type, "optional", default)
                     inheritance.extend(super)
                     attributes.update(attrs)
                     attributes["order"] = order
@@ -315,28 +325,107 @@
     """
     Funtion that returns the Python type and default value for a given type
     """
-    def GetTypeInitialValue(self, attr_type):
+    def GetTypeInitialValue(self, attr_type, default = None):
         type_compute = attr_type[4:].replace("[]", "")
         if attr_type.startswith("bse:"):
             if type_compute == "boolean":
-                return BooleanType, "False"
-            elif type_compute in ["decimal","unsignedLong","long","integer"]:
-                return IntType, "0"
+                if default:
+                    def GetBooleanInitialValue():
+                        return default == "true"
+                    return BooleanType, GetBooleanInitialValue
+                else:
+                    return BooleanType, lambda:False
+            elif type_compute in ["unsignedLong","long","integer"]:
+                if default:
+                    def GetIntegerInitialValue():
+                        return int(default)
+                    return IntType, GetIntegerInitialValue
+                else:
+                    return IntType, lambda:0
+            elif type_compute == "decimal":
+                if default:
+                    def GetFloatInitialValue():
+                        return float(default)
+                    return FloatType, GetFloatInitialValue
+                else:
+                    return FloatType, lambda:0.
             elif type_compute in ["string","anyURI","NMTOKEN"]:
-                return StringType, "\"\""
+                if default:
+                    def GetStringInitialValue():
+                        return default
+                    return StringType, GetStringInitialValue
+                else:
+                    return StringType, lambda:""
             elif type_compute == "time":
-                return TimeType, "time(0,0,0,0)"
+                if default:
+                    def GetTimeInitialValue():
+                        result = time_model.match(value)
+                        if result:
+                            values = result.groups()
+                            time_values = [int(v) for v in values[:2]]
+                            seconds = float(values[2])
+                            time_values.extend([int(seconds), int((seconds % 1) * 1000000)])
+                            return time(*time_values)
+                        return time(0,0,0,0)
+                    return TimeType, GetTimeInitialValue
+                else:
+                    return TimeType, lambda:time(0,0,0,0)
             elif type_compute == "date":
-                return DateType, "date(1,1,1)"
+                if default:
+                    def GetDateInitialValue():
+                        result = date_model.match(value)
+                        if result:
+                            date_values = [int(v) for v in result.groups()]
+                            return date(*date_values)
+                        return date(1,1,1)
+                    return DateType, GetDateInitialValue
+                else:
+                    return DateType, lambda:date(1,1,1)
             elif type_compute == "dateTime":
-                return DateTimeType, "datetime(1,1,1,0,0,0,0)"
+                if default:
+                    def GetDateTimeInitialValue():
+                        result = datetime_model.match(value)
+                        if result:
+                            values = result.groups()
+                            datetime_values = [int(v) for v in values[:5]]
+                            seconds = float(values[5])
+                            datetime_values.extend([int(seconds), int((seconds % 1) * 1000000)])
+                            return datetime(*datetime_values)
+                        return datetime(1,1,1,0,0,0,0)
+                    return DateTimeType, GetDateTimeInitialValue
+                else:
+                    return DateTimeType, lambda:datetime(1,1,1,0,0,0,0)
             elif type_compute == "language":
-                return StringType, "\"en-US\""
+                if default:
+                    def GetStringInitialValue():
+                        return default
+                    return StringType, GetStringInitialValue
+                else:
+                    return StringType, lambda:"en-US"
             else:
                 print "Can't affect: %s"%type_compute
         elif attr_type.startswith("cls:"):
             if self.XMLClassDefinitions.get(type_compute, None) != None:
-                return self.XMLClassDefinitions[type_compute],"%s()"%type_compute
+                def GetClassInitialValue():
+                    if self.XMLClassDefinitions.get(type_compute, None) != None:
+                        obj = self.ComputedClasses[type_compute]()
+                        if default:
+                            obj.setValue(default)
+                        return obj
+                    return None
+                return self.XMLClassDefinitions[type_compute], GetClassInitialValue
+        return None, lambda:None
+
+    """
+    Funtion that returns the Python type and default value for a given type
+    """
+    def GetInitialValues(self, value_types):
+        initial_values = {}
+        for name, value_type in value_types.items():
+            result = self.GetTypeInitialValue(value_type)
+            if result:
+                initial_values[name] = result[1]
+        return initial_values
 
     """
     Methods that generate the classes
@@ -396,20 +485,20 @@
                     # Class is a enumerated type
                     elif attr == "enum":
                         value_type, initial = self.GetTypeInitialValue(members["basetype"])
-                        initialValues["value"] = "\"%s\""%values[0]
+                        initialValues["value"] = GetInitialValueFunction(values[0])
                         classmembers["value"] = values[0]
                         classmembers["setValue"] = generateSetEnumMethod(values, value_type)
                         classmembers["getValue"] = generateGetMethod("value")
+                        classmembers["getValidValues"] = generateGetChoicesMethod(values)
                     
                     # Class is a limited type
                     elif attr == "limit":
                         value_type, initial = self.GetTypeInitialValue(members["basetype"])
-                        initial = 0
                         if "min" in values:
                             initial = max(initial, values["min"])
                         if "max" in values:
                             initial = min(initial, values["max"])
-                        initialValues["value"] = "%d"%initial
+                        initialValues["value"] = GetInitialValueFunction(initial)
                         classmembers["value"] = initial
                         classmembers["setValue"] = generateSetLimitMethod(values, value_type)
                         classmembers["getValue"] = generateGetMethod("value")
@@ -417,29 +506,33 @@
                     # Class has an attribute that can have different value types
                     elif attr == "choice_content":
                         classmembers["content"] = None
-                        initialValues["content"] = "None"
+                        initialValues["content"] = lambda:None
                         classmembers["deleteContent"] = generateDeleteMethod("content")
+                        classmembers["addContent"] = generateAddChoiceMethod(values, self.GetInitialValues(values))
                         classmembers["setContent"] = generateSetChoiceMethod(values)
                         classmembers["getContent"] = generateGetMethod("content")
+                        classmembers["getChoices"] = generateGetChoicesMethod(values)
                     elif attr == "multichoice_content":
                         classmembers["content"] = []
-                        initialValues["content"] = "[]"
+                        initialValues["content"] = lambda:[]
                         classmembers["appendContent"] = generateAppendChoiceMethod(values)
+                        classmembers["appendContentByType"] = generateAppendChoiceByTypeMethod(values, self.GetInitialValues(values))
                         classmembers["insertContent"] = generateInsertChoiceMethod(values)
                         classmembers["removeContent"] = generateRemoveMethod("content")
                         classmembers["countContent"] = generateCountMethod("content")
                         classmembers["setContent"] = generateSetMethod("content", ListType)
                         classmembers["getContent"] = generateGetMethod("content")
+                        classmembers["getChoices"] = generateGetChoicesMethod(values)
                     
                     # It's an attribute of the class
                     else:
                         attrname = attr[0].upper()+attr[1:]
-                        attr_type, xml_type, write_type = values
-                        value_type, initial = self.GetTypeInitialValue(attr_type)
+                        attr_type, xml_type, write_type, default = values
+                        value_type, initial = self.GetTypeInitialValue(attr_type, default)
                         # Value of the attribute is a list
                         if attr_type.endswith("[]"):
                             classmembers[attr] = []
-                            initialValues[attr] = "[]"
+                            initialValues[attr] = lambda:[]
                             classmembers["append"+attrname] = generateAppendMethod(attr, value_type)
                             classmembers["insert"+attrname] = generateInsertMethod(attr, value_type)
                             classmembers["remove"+attrname] = generateRemoveMethod(attr)
@@ -448,18 +541,18 @@
                         else:
                             if write_type == "optional":
                                 classmembers[attr] = None
-                                initialValues[attr] = "None"
-                                classmembers["add"+attrname] = generateAddMethod(attr, initial, self.ComputedClasses)
+                                initialValues[attr] = lambda:None
+                                classmembers["add"+attrname] = generateAddMethod(attr, initial)
                                 classmembers["delete"+attrname] = generateDeleteMethod(attr)
                             else:
-                                classmembers[attr] = initial
+                                classmembers[attr] = initial()
                                 initialValues[attr] = initial
                             classmembers["set"+attrname] = generateSetMethod(attr, value_type)
                         classmembers["get"+attrname] = generateGetMethod(attr)
-                classmembers["__init__"] = generateInitMethod(bases, initialValues, self.ComputedClasses)
+                classmembers["__init__"] = generateInitMethod(bases, initialValues)
                 classmembers["loadXMLTree"] = generateLoadXMLTree(bases, members, self.ComputedClasses)
                 classmembers["generateXMLText"] = generateGenerateXMLText(bases, members)
-                classmembers["getElementAttributes"] = generategetElementAttributes(bases, members)
+                classmembers["getElementAttributes"] = generateGetElementAttributes(bases, members, self.ComputedClasses)
                 classmembers["singleLineAttributes"] = True
                 
                 self.ComputedClasses[classname] = classobj(classname, bases, classmembers)
@@ -528,7 +621,7 @@
             # Load the node attributes if they are defined in the list
             for attrname, attr in tree._attrs.items():
                 if attrname in members.keys():
-                    attr_type, xml_type, write_type = members[attrname]
+                    attr_type, xml_type, write_type, default = members[attrname]
                     attr_value = GetAttributeValue(attr)
                     if write_type != "optional" or attr_value != "":
                         # Extracts the value
@@ -587,7 +680,7 @@
                     
                     # The node child is defined in the list
                     elif name in members.keys():
-                        attr_type, xml_type, write_type = members[name]
+                        attr_type, xml_type, write_type, default = members[name]
                         # Extracts the value
                         if attr_type.startswith("bse:"):
                             attr_value = GetAttributeValue(node)
@@ -623,10 +716,10 @@
             order = members["order"]
         else:
             order = []
-        if "choice_content" in members.keys() and "choice_content" not in order:
-            order.append("choice_content") 
-        if "multichoice_content" in members.keys() and "multichoice_content" not in order:
-            order.append("multichoice_content") 
+        for attr, values in members.items():
+            if attr != "order" and (attr in ("choice_content", "multichoice_content") or values[1] != "attribute"):
+                if attr not in order:
+                    order.append(attr)
         size = 0
         first = True
         for attr, value in extras.items():
@@ -645,21 +738,16 @@
                 return text
             elif values[1] == "attribute":
                 value = getattr(self, attr, None)
-                if value == "":
-                    value = None
-                if values[2] != "optional" or value != None:
-                    if not first and not self.singleLineAttributes:
-                        text += "\n%s"%(ind2)
-                    if values[0].startswith("cls"):
-                        if len(bases) > 0:
-                            base_extras[attr] = value.getValue()
-                        else:
-                            text += " %s=\"%s\""%(attr, ComputeValue(value.getValue()))
+                if values[0].startswith("cls"):
+                    value = value.getValue()
+                computed_value = ComputeValue(value)
+                if values[2] != "optional" or value != computed_value:
+                    if len(bases) > 0:
+                        base_extras[attr] = value
                     else:
-                        if len(bases) > 0:
-                            base_extras[attr] = value
-                        else:
-                            text += " %s=\"%s\""%(attr, ComputeValue(value))
+                        if not first and not self.singleLineAttributes:
+                            text += "\n%s"%(ind2)
+                        text += " %s=\"%s\""%(attr, computed_value)
                     first = False
         if len(bases) > 0:
             first, new_text = bases[0].generateXMLText(self, name, indent, base_extras, True)
@@ -726,31 +814,37 @@
     return generateXMLTextMethod
 
 
-def generategetElementAttributes(bases, members):
+def generateGetElementAttributes(bases, members, classes):
     def getElementAttributes(self):
         attr_list = []
         for attr, values in members.items():
             if attr in ["order","choice_content","multichoice_content"]:
                 pass
             elif values[1] == "attribute":
-                if values[2] == "required":
-                    require = True
-                else:
-                    require = False
-                attr_hash ={"name": attr,"type": values[0] ,"value": getattr(self, attr, "") ,"require": require}
-                attr_list.append(attr_hash)
+                attr_params = {"name": attr, "require": values[2] == "required"}
+                if values[0].startswith("cls:"):
+                    attr_value = getattr(self, attr, None)
+                    if attr_value:
+                        attr_params["value"] = attr_value.getValue()
+                    else:
+                        attr_params["value"] = ""
+                    attr_params["type"] = classes[values[0][4:]]().getValidValues()
+                else:
+                    attr_params["value"] = getattr(self, attr, "")
+                    attr_params["type"] = values[0][4:]
+                attr_list.append(attr_params)
         return attr_list
     return getElementAttributes
     
 """
 Methods that generates the different methods for setting and getting the attributes
 """
-def generateInitMethod(bases, members, classes):
+def generateInitMethod(bases, members):
     def initMethod(self):
         for base in bases:
             base.__init__(self)
         for attr, initial in members.items():
-            setattr(self, attr, eval(initial, globals().update(classes)))
+            setattr(self, attr, initial())
     return initMethod
 
 def generateSetMethod(attr, attr_type):
@@ -758,11 +852,22 @@
         setattr(self, attr, value)
     return setMethod
 
+def generateAddChoiceMethod(choice_type, initial_values):
+    def addChoiceMethod(self, name):
+        if name in choice_type:
+            self.content = {"name" : name, "value" : initial_values[name]()}
+    return addChoiceMethod
+
 def generateSetChoiceMethod(choice_type):
     def setChoiceMethod(self, name, value):
-        self.content = {"name":name,"value":value}
+        self.content = {"name" : name, "value" : value}
     return setChoiceMethod
 
+def generateGetChoicesMethod(choice_type):
+    def getChoicesMethod(self):
+        return choice_type
+    return getChoicesMethod
+
 def generateSetEnumMethod(enum, attr_type):
     def setEnumMethod(self, value):
         if value in enum:
@@ -786,9 +891,9 @@
         return getattr(self, attr, None)
     return getMethod
 
-def generateAddMethod(attr, initial, classes):
+def generateAddMethod(attr, initial):
     def addMethod(self):
-        setattr(self, attr, eval(initial, globals().update(classes)))
+        setattr(self, attr, initial())
     return addMethod
 
 def generateDeleteMethod(attr):
@@ -806,6 +911,12 @@
         getattr(self, attr).insert(index, value)
     return insertMethod
 
+def generateAppendChoiceByTypeMethod(choice_type, initial_values):
+    def addChoiceMethod(self, name):
+        if name in choice_type:
+            self.content.append({"name" : name, "value" : initial_values[name]()})
+    return addChoiceMethod
+
 def generateAppendChoiceMethod(choice_types):
     def appendMethod(self, name, value):
         self.content.append({"name":name,"value":value})
@@ -836,6 +947,7 @@
             sys._getframe(1).f_locals[ClassName] = Class
         for TypeName, Type in pluginTypes.items():
             sys._getframe(1).f_locals[TypeName] = Type
+    globals().update(ComputedClasses)
     return ComputedClasses, ComputedTypes
 
 """
@@ -854,4 +966,3 @@
     factory = ClassFactory(minidom.parseString(xsdstring))
     return GenerateClasses(factory, declare)
 
-