prop.py :  » Development » SnapLogic » snaplogic » cc » Python Open Source

Home
Python Open Source
1.3.1.2 Python
2.Ajax
3.Aspect Oriented
4.Blog
5.Build
6.Business Application
7.Chart Report
8.Content Management Systems
9.Cryptographic
10.Database
11.Development
12.Editor
13.Email
14.ERP
15.Game 2D 3D
16.GIS
17.GUI
18.IDE
19.Installer
20.IRC
21.Issue Tracker
22.Language Interface
23.Log
24.Math
25.Media Sound Audio
26.Mobile
27.Network
28.Parser
29.PDF
30.Project Management
31.RSS
32.Search
33.Security
34.Template Engines
35.Test
36.UML
37.USB Serial
38.Web Frameworks
39.Web Server
40.Web Services
41.Web Unit
42.Wiki
43.Windows
44.XML
Python Open Source » Development » SnapLogic 
SnapLogic » snaplogic » cc » prop.py
# $SnapHashLicense:
# 
# SnapLogic - Open source data services
# 
# Copyright (C) 2009, SnapLogic, Inc.  All rights reserved.
# 
# See http://www.snaplogic.org for more information about
# the SnapLogic project. 
# 
# This program is free software, distributed under the terms of
# the GNU General Public License Version 2. See the LEGAL file
# at the top of the source tree.
# 
# "SnapLogic" is a trademark of SnapLogic, Inc.
# 
# 
# $
# $Id: prop.py 9195 2009-10-12 20:41:19Z dmitri $

import sys
import copy
from decimal import Decimal
from datetime import datetime
from sets import ImmutableSet

from snaplogic.common.snap_exceptions import *
from snaplogic.snapi_base import resdef,keys


"""
User defined properties can be based on simple types like number, string, boolean or datetime. Such properties
are defined by the class SimpleProp. On the other hand, a property can be more complex and can be defined
using a list or a dictionary of SimpleProps. These lists or dictionaries could then be nested in other
lists and dictionaries to create arbitrarily complex properties.


Company :{
                                     EmpList : [
                      Employee: {
                                 Name:
                                 Sex:
                                 Age:
                                 Address: {
                                           street number:
                                           street name:
                                           City:
                                           State:
                                           zip:
                                          }
                                 }
                                      ]
                  }
This can be defined as property as follows:
addr_def = DictProp('Address Info', None, '', 4, 4)
addr_def['street number'] = SimpleProp('Street Number', number)
addr_def['street name'] = SimpleProp('Street Name', string)
addr_def['state'] = SimpleProp('State', string)
addr_def['city'] = SimpleProp('City', string)
addr_def['zip'] = SimpleProp('zip', number)

emp_def = DictProp('Employee Info', None, '', 4, 4)
emp_def['Name'] = SimpleProp('Name', string)
emp_def['Sex'] = SimpleProp('Sex', string)
emp_def['Age'] = SimpleProp('Age', number)
emp_def['Address'] = addr_def

emplist_def = ListProp('List of employees', emp_def, '', 0, 1000):

comp_def = DictProp('Company Info')
comp_def['EmpList'] = emplist_def
self.set_propertydef('Company', comp_def)

This definition code is not trivial, but the aim was to provide a reasonable interface for the most
common case (which is simple property) and provide a scalable mechanism for component developers
who need to do something more involved. The values of these user defined properties can set or got
by using the following methods

get_property_value(prop_name)
set_propertyval(prop_name, prop_val)

For the above structure, here are some sample uses:
v = self.get_property_value('Company')
v['EmplList'].append({'Name':'John Doe',
                                     'Sex':'M',
                                     'Age':39
                                     'Address':{'...}})
self.set_propertyval('Company', v)
"""

# This dictionary maps the constraints to error messages that constraint validation should produce
# if constraint is violated. 
LOV_ERROR_MESSAGES = {
                      ImmutableSet([keys.CONSTRAINT_LOV_INPUT_VIEW]) : "Expected an input view name: %s",
                      ImmutableSet([keys.CONSTRAINT_LOV_OUTPUT_VIEW]) : "Expected an output view name: %s",
                      ImmutableSet([keys.CONSTRAINT_LOV_INPUT_VIEW, keys.CONSTRAINT_LOV_OUTPUT_VIEW]) : "Expected a view name: %s",
                      ImmutableSet([keys.CONSTRAINT_LOV_INPUT_FIELD]) : "Expected an input field name: %s",
                      ImmutableSet([keys.CONSTRAINT_LOV_OUTPUT_FIELD]) : "Expected an output field name: %s",
                      ImmutableSet([keys.CONSTRAINT_LOV_INPUT_FIELD, keys.CONSTRAINT_LOV_OUTPUT_FIELD]) : "Expected a field name: %s",
                      } 

    
class SimpleProp(object):
    
    def __init__(self, label_str, simple_type, description_str = "", prop_constraint = None, required = False):
        """
        Initializes a simple type property defintion.
        
        Simple type properties are of the type number, string, datetime and boolean.
        
        @param label_str: The label (UI friendly) string.
        @type label_str:  str
        
        @param simple_type: The type of the property. Can be "number", "string", "datetime" and "boolean"
        @type simple_type:  str
        
        @param description_str: Document string describing the property.
        @type description_str:  str
        
        @param prop_constraint: Dictionary of constraints.
        @type prop_constraint:  dict
        
        @param required: If True, then this property must exist. If this property is nested inside
            another, then the property is required only if the enclosing property exists.
        @type required:  bool
        
        
        """
        
        if label_str is None:
            label_str = "Simple Property"

        self.label_str = label_str
        if simple_type not in resdef.type_and_constraints.keys():
            raise SnapResDefError("%s is not a valid type" % simple_type)
        self.simple_type = simple_type
        self.description_str = description_str
        
        resdef.validate_constraint(simple_type, prop_constraint)
        self.prop_constraint = prop_constraint
        self.required = required

    def __str__(self):
        s = []
        s.append("Label: %s, Type: %s, Required: %s " % (self.label_str, self.simple_type, self.required))
        s.append("Prop Constraint: %s, Description: %s\n" % (self.prop_constraint, self.description_str))
        return "".join(s)
    
    def __repr__(self):
        return self.__str__()
    
    def __eq__(self, s):
        
        if ((not hasattr(s, "label_str")       or self.label_str != s.label_str) or
            (not hasattr(s, "simple_type")     or self.simple_type != s.simple_type) or
            (not hasattr(s, "prop_constraint") or self.prop_constraint != s.prop_constraint) or
            (not hasattr(s, "description_str") or self.description_str != s.description_str) or
            (not hasattr(s, "required")        or self.required != s.required)):
            return False
        else:
            return True
    
    def __ne__(self, s):
        return not self.__eq__(s)
    
    def _to_resdef(self):
        return resdef.get_resdef_format_simple_prop(self.label_str, self.simple_type,
                                                    self.description_str, self.prop_constraint,
                                                    self.required)
    
    def validate(self, data, my_key = None, partial_data = False, all_dynamic_constraints = None):
        """
        Validate the simple value using this simple prop class.
        
        @param data: Data to be validated
        @type data:  simple values
        
        @param my_key: If this value is an entry inside another dict or list, then the key for the
            entry can be specified in this param. Used for creating more discriptive error messages.
        @type my_key:  str
        
        @param partial_data: If set to True, then don't return error if all the values have not been provided.
             For example, required property values may not be specified. So, just 
        @type partial_data:  bool
        
        @param all_dynamic_constraints: Dictionary mapping dynamic constraint name to constraint value.
             For example, dynamic constraint "input field" may look like:
             {keys.CONSTRAINT_LOV_INPUT_FIELD : ['city', 'state', 'zip'] }
             Meaning that there are three input fields: city, state, and zip.
        @type all_dynamic_constraints: dict
        
        @return: If the value is not valid, then an error string is returned, else None is returned.
        @rtype:  str
        
        """
        if data is None or (self.simple_type == "string" and data == ""):
            if (not partial_data) and self.required:
                return "Property '%s' requires a value" % self.label_str
            else:
                return None
            
        # If there is an LOV constraint, expand
        # dynamic constraints (e.g. "input field") into a list of values
        # and pass that to validate_simple_prop_value.
        prop_constraint = self.prop_constraint
        custom_lov_error_message = None

        if prop_constraint and 'lov' in prop_constraint:
            # Make a copy of the constraint because we'll be changing it
            # (expanding the list) -- so we don't overwrite the resdef.
            prop_constraint = copy.copy(self.prop_constraint)
            lov_constraint = []

            # Used dynamic constraints lists all dynamic constraints used:
            # for looking up the proper error message should the constraint not validate.
            used_dynamic_constraints = []
            for value in prop_constraint['lov']:
                if value not in keys.CONSTRAINT_LOV_DYNAMIC_VALUES:
                    lov_constraint.append(value)
                else:
                    used_dynamic_constraints.append(value)
                    lov_constraint.extend(all_dynamic_constraints[value])

            custom_lov_error_message = LOV_ERROR_MESSAGES.get(ImmutableSet(used_dynamic_constraints))
            if custom_lov_error_message:
                custom_lov_error_message = custom_lov_error_message % (', '.join(lov_constraint)) 

            prop_constraint['lov'] = lov_constraint 
        
        ret = resdef.validate_simple_prop_value(self.simple_type, data, prop_constraint, custom_lov_error_message)
        if ret is not None:
            return "Value for '%s' is invalid. %s" % (self.label_str, ret)
        
        return None
        
    
    
DEFAULT_PROPERTY_TYPE = SimpleProp("Property", "string",)
        
        
class ListProp(object):
    """
    A list property object provides a defintion of list container whose values are SimpleProp, ListProp or DictProp
    definitions. All the normal list methods are supported, with the constraint that the specified max_size is not
    exceeded.
    
    """
    
    def __init__(self, label_str, default_entry_type = None, description_str = None,
                 min_size = 0, max_size = resdef.UNLIMITED_ENTRIES, required = False):
        """
        Initializes a list type property definition.
        
        @param label_str: The label (UI friendly) string.
        @type label_str:  str
        
        @param default_entry_type: The type for entries in the list. Can be SimpleProp, ListProp or
            DictProp definition. If no default value is specified, then string SimpleProp is assumed.
        @type default_entry_type:  L{SimpleProp} or L{ListProp} or L{DictProp}
        
        @param description_str: Document string describing the property.
        @type description_str:  str
        
        @param min_size: Minimum size of the list. Default value is 0.
        @type min_size:  int
        
        @param max_size: Maximum size of the list. Default value is infinite.
        @type max_size:  int
        
        @param required: If True, then this property must exist. If this property is nested inside
            another, then the property is required only if the enclosing property exists.
        @type required:  bool
        
        """
        
        if label_str is None:
            label_str = "List Property"
            
        self.label_str = label_str
        if default_entry_type is None:
            default_entry_type = DEFAULT_PROPERTY_TYPE
        elif ((not isinstance(default_entry_type, SimpleProp)) and (not isinstance(default_entry_type, DictProp))
                                                             and (not isinstance(default_entry_type, ListProp))):
            raise SnapResDefError("The default_entry_type needs to be a SimpleProp/ListProp/DictProp object")
        
        self.default_entry_type = default_entry_type
        self.description_str = description_str
        
        resdef.validate_limits(min_size, max_size)
        self.min_size = min_size
        self.max_size = max_size
        self.required = required

    def __str__(self):
        l = []    
        l.append("LIST TYPE:\nLabel: %s, Description: %s Min size: %s Max size: %s Required: %s\n" 
                 % (self.label_str, self.description_str, self.min_size, self.max_size, self.required))
        l.append("Default entry:\n%s\n" % str(self.default_entry_type))
        
        return "".join(l)
        
    def __eq__(self, s):
        
        if ((not hasattr(s, "label_str")          or self.label_str != s.label_str) or
            (not hasattr(s, "description_str")    or self.description_str != s.description_str) or
            (not hasattr(s, "default_entry_type") or self.default_entry_type != s.default_entry_type) or
            (not hasattr(s, "min_size")           or self.min_size != s.min_size) or
            (not hasattr(s, "max_size")           or self.max_size != s.max_size) or
            (not hasattr(s, "required")           or self.required != s.required)):
            return False
        else:
            return True
    
    def __ne__(self, s):
        return not self.__eq__(s)
    
    def _is_int(self, i):
        """
        Raise exception if param is not int.
        
        @param i: Value to be tested
        @type i:  anything
        
        @raise TypeError: If param is not an integer.
        
        """
        invalid = False
        try:
            if int(i) != i:
                invalid = True
        except (TypeError, ValueError):
            raise TypeError("list indices must be integers")
        
        if invalid:
            raise TypeError("list indices must be integers")

    def _to_resdef(self):
        r = resdef.get_resdef_format_list_prop(self.label_str, self.default_entry_type._to_resdef(), 
                                               self.description_str, self.min_size, self.max_size,
                                               self.required)
        r[keys.COMPOSITE_PROPERTY_STRUCT] = []
        
        return r
        
    def get_item_type(self, idx):
        """
        This method returns the expected type of this list entry.
        
        The method checks to see if the list definition has an explicitly specified type for the
        item specified by the index number and returns it. If no explicit definition was found,
        then default type for the list is returned.
        
        @param idx: Index number
        @type idx:  int or long
        
        @return: Expected type of the list entry.
        @rtype:  L{SimpleProp}, L{ListProp}, L{DictProp}
        
        """
        
        # Do a quick check to make sure index is int
        self._is_int(idx)
        if idx > self.max_size:
            raise IndexError("Values %s is outside the valid range for this ListProp (%s-%s)" %
                             (idx, self.min_size, self.max_size))
        else:
            return self.default_entry_type
    
    def validate(self, data, my_key = None, partial_data = False, all_dynamic_constraints = None):
        """
        Validate the list value using this list prop class.
        
        @param data: Tha data to be validated
        @type data:  list
        
        @param my_key: If this list is a nested entry inside another dict or list, then the key for the
            entry can be specified in this param. Used for creating more discriptive error messages.
        @type my_key:  str
        
        @param partial_data: If set to True, then don't return error if all the required entries have not
            been provided or lower limit in the list has not been reached.
        @type partial_data:  bool
        
        @param all_dynamic_constraints: See SimpleProp documentation for description of this parameter.
                                        This parameter is not used here.  It is only used with SimpleProp types.
                                        The reason it's not used here is because it is only needed for validating 
                                        dynamic LOV constraints (see keys.CONSTRAINT_LOV_DYNAMIC_VALUES).
                                        These constraints are only used with SimpleProp.
                                        However even though this parameter isn't used it's provided here
                                        to keep the signature of the validate method the same across
                                        all property types (SimpleProp, ListProp, DictProp).
        
        @return: If the list is not valid, then an error string is returned, else None is returned.
        @rtype:  str
        
        """
        
        if data is None:
            if (not partial_data) and self.required:
                return "Property '%s' requires a value" % self.label_str
            else:
                return None
            
        # Quick check to make sure its a list/tuple like object.
        if not isinstance(data, list) and not isinstance(data, tuple):
            return "%s: is supposed to be a list or tuple, received %s value - %s" \
                    % (self.label_str, type(data), data)
        
        l = len(data)
        if l > self.max_size:
             return "%s: Length '%s' exceeds the maximum allowed '%s' for the list" % (self.label_str, l, self.max_size)
            
        if (not partial_data) and l < self.min_size:
            return "%s: Length '%s' is less than the minimum allowed '%s' for the list" % \
                (self.label_str, l, self.min_size)
                   
        return None
            
            
class DictProp(dict):
    """
    A dictionary property provides a dictionary whose values are SimpleProp, ListProp or DictProp.
    The key is string or unicode. All the normal dictionary methods are supported, with the constraint
    that the specified max_size is not exceeded.
    
    """
    def __init__ (self, label_str, default_entry_type = None, description_str = None, min_size = 0,
                  max_size = resdef.UNLIMITED_ENTRIES, fixed_keys = False, required = False):
        """
        Initializes a dict type property defintion.
        
        @param label_str: The label (UI friendly) string.
        @type label_str:  str
        
        @param default_entry_type: The default type for entries in the dict. Can be  SimpleProp, ListProp or
            DictProp definition. If no default value is specified, then string SimpleProp is used.
        @type default_entry_type:  L{SimpleProp} or L{ListProp} or L{DictProp}
        
        @param description_str: Document string describing the property.
        @type description_str:  str
        
        @param min_size: Minimum size of the dict. Default value is 0.
        @type min_size:  int
        
        @param max_size: Maximum size of the dict. Default value is infinite.
        @type max_size:  int
        
        @param fixed_keys: Set to True, if each entry in the dictionary is explicitly defined. The max/min
            limits are ignored if this is set to True.
        @type fixed_keys:  bool
        
        @param required: If True, then this property must exist. If this property is nested inside
            another, then the property is required only if the enclosing property exists.
        @type required:  bool
        
        """
        dict.__init__(self)
        
        if label_str is None:
            label_str = "Dict Property"
            
        self.label_str = label_str
        
        if default_entry_type is None:
            default_entry_type = DEFAULT_PROPERTY_TYPE
        elif ((not isinstance(default_entry_type, SimpleProp)) and (not isinstance(default_entry_type, DictProp))
                                                             and (not isinstance(default_entry_type, ListProp))):
            raise SnapResDefError("The default_entry_type needs to be a SimpleProp/ListProp/DictProp object")
        self.default_entry_type = default_entry_type
        
        self.description_str = description_str
        
        resdef.validate_limits(min_size, max_size)
        self.min_size = min_size
        self.max_size = max_size
        self.fixed_keys = fixed_keys
        self.required = required
    
    def __str__(self):
        
        l = [] 
        l.append("DICT TYPE:\nLabel: %s, Description %s Min size %s Max size %s FixedKeys %s Required %s\n" 
                 % (self.label_str, self.description_str, self.min_size, self.max_size, self.fixed_keys, self.required))
        l.append("Default entry %s" % str(self.default_entry_type))
        for s in self:
            l.append("Entry: %s->%s" % (s, self[s]))
        
        return "".join(l)
    
    def __eq__(self, s):
        
        if ((not hasattr(s, "label_str")          or self.label_str != s.label_str) or
            (not hasattr(s, "description_str")    or self.description_str != s.description_str) or
            (not hasattr(s, "default_entry_type") or self.default_entry_type != s.default_entry_type) or
            (not hasattr(s, "min_size")           or self.min_size != s.min_size) or
            (not hasattr(s, "max_size")           or self.max_size != s.max_size) or
            (not hasattr(s, "required")           or self.required != s.required) or
            (not hasattr(s, "fixed_keys")         or self.fixed_keys != s.fixed_keys) or
            dict(self) != dict(s)):
            return False
        else:
            return True
    
    def __ne__(self, s):
        return not self.__eq__(s)
    
    def __setitem__(self, key, value):
        """
        Intercept the l[key]=value operations.
        
        @param key: Key for the entry
        @type key:  str
        
        @param value: Value for the entry
        @type value:  Varies, can  be SimpleProp or a more complex property like DictProp or ListProp.
        
        """
        if ((value is not None) and (not isinstance(value, SimpleProp)) and (not isinstance(value, DictProp))
                                                                        and (not isinstance(value, ListProp))):
            raise SnapResDefError("The value needs to be a SimpleProp/ListProp/DictProp/None object")
        if key not in self and len(self) >= self.max_size:
            raise SnapResDefError("New value exceed upper limit (%s) for entries in the dictionary" % self.max_size)
        dict.__setitem__(self, key, value)
    
    def update(self, update_dict):
        """
        Update method of a dictionary container.
        
        @param update_dict: The dictionary being used for update
        @type update_dict:  L{DictProp}
        
        """
        
        i = 0
        for key, value in update_dict.items():
            if key not in self:
                i += 1
            if ((value is not None) and (not isinstance(value, SimpleProp)) and (not isinstance(value, DictProp))
                                                                            and (not isinstance(value, ListProp))):
                raise SnapResDefError("The value needs to be a SimpleProp/ListProp/DictProp/None object")
        if len(self) + i > self.max_size:
            raise SnapResDefError("New values exceed upper limit (%s) for entries in the dictionary" % self.max_size)
        dict.update(self, update_dict)
    
    def setdefault(self, key, value = None):
        """
        The setdefault functionality of a dictionary.
        
        If None is specified for value, then the default will be set to the default
        type definition provided to the dictionary at initialization time (not None).
        
        @param key: Key for the setdefault call
        @type key:  str
        
        @param value: The default value. If not specified or set to None, then the default_entry_type is
            used.
        @type value:  None or L{SimpleProp}, L{DictProp} or L{ListProp}
        
        @return: The value at the key of default entry.
        @rtype:  L{SimpleProp} or L{DictProp} or L{ListProp}
        
        """
        
        if value is None:
            value = self.default_entry_type
        elif ((not isinstance(value, SimpleProp)) and (not isinstance(value, DictProp))
                                                  and (not isinstance(value, ListProp))):
            raise SnapResDefError("The value needs to be a SimpleProp/ListProp/DictProp object")
        if key not in self and len(self) == self.max_size:
            raise SnapResDefError("New value exceeds upper limit (%s) for entries in the dictionary" % self.max_size)
            
        return dict.setdefault(self, key, value)
    
    def _to_resdef(self):
        
        r = resdef.get_resdef_format_dict_prop(self.label_str, self.default_entry_type._to_resdef(),
                                               self.description_str, self.min_size, self.max_size,
                                               self.fixed_keys, self.required)
        for k in self:
            r[keys.COMPOSITE_PROPERTY_STRUCT][k] = self[k]._to_resdef()
        
        return r
   
    def get_item_type(self, key):
        """
        This method returns the expected type of this dict entry.
        
        The method checks to see if the dict definition has an explicitly specified type for the
        item and returns it. If no explicit definition was found, then default type for the dict
        is returned.
        
        @param key: Key of the item
        @type key:  str
        
        @return: Expected type of the dict entry.
        @rtype:  L{SimpleProp}, L{ListProp}, L{DictProp}
        
        """
        
        if key in self:
            return self[key]
        else:
            return self.default_entry_type
        
    def validate(self, data, my_key = None, partial_data = False, all_dynamic_constraints = None):
        """
        Validate the dictionary value using this dict prop class.
        
        @param data: The data to be validated
        @type data:  dict
        
        @param my_key: If this dict is a nested entry inside another dict or list, then the key for the
            entry can be specified in this param. Used for creating more discriptive error messages.
        @type my_key:  str
        
        @param partial_data: If set to True, then don't return error if all the required entries have not
            been provided or lower limit in the dict has not been reached.
        @type partial_data:  bool
        
        @param all_dynamic_constraints: See SimpleProp documentation for description of this parameter.
                                        This parameter is not used here.  It is only used with SimpleProp types.
                                        The reason it's not used here is because it is only needed for validating 
                                        dynamic LOV constraints (see keys.CONSTRAINT_LOV_DYNAMIC_VALUES).
                                        These constraints are only used with SimpleProp.
                                        However even though this parameter isn't used it's provided here
                                        to keep the signature of the validate method the same across
                                        all property types (SimpleProp, ListProp, DictProp).
        
        @return: If the dict is not valid, then an error string is returned, else None is returned.
        @rtype:  str
        
        """
        
        if data is None:
            if (not partial_data) and self.required:
                return "Property '%s' requires a value" % self.label_str
            else:
                return None
        
        # Quick check to make sure its a dict like object.
        if not (hasattr(data, "keys") and callable(data.keys)):
            return "%s: Should be a dictionary, received %s value - %s" % (self.label_str, type(data), data)
        
        l = len(data)
        if l > self.max_size:
            return "%s: Length (%s) greater than the maximum allowed (%s) for the dictionary" % \
                    (self.label_str, l, self.max_size)
            
        if (not partial_data) and l < self.min_size:
            return "%s: Length (%s) less than the minimum allowed (%s) for the dictionary" % \
                    (self.label_str, l, self.min_size)
        
        # Make sure all required entries are present in the dictionary
        for k in self:
            if self[k] is None:
                # This entry has been left undefined.
                continue
            if (not partial_data) and self[k].required:
                if k not in data:
                    return "%s: Missing required key %s" % (self.label_str, k)
                """
                elif data[k] is None:
                    # The error message has been made more user friendly as per #1284. This message (unlike many
                    # others in this method) can occur frequently due to operator error and should have a message
                    # which is user friendly.
                    return "Property '%s' requires a value" % k
                """
        if self.fixed_keys:
            for k in data:
                if k not in self:
                    return "%s: Has unexpected key '%s'" % (self.label_str, k)
                
        return None

def create_simple_prop(r):
    if r[keys.PROPERTY_TYPE] not in resdef.list_of_simple_types:
         raise SnapResDefError("The resdef type '%s' is not simple type" % r[keys.PROPERTY_TYPE])
    simple_type = r[keys.PROPERTY_TYPE]
    label_str = r[keys.LABEL]
    description_str = r[keys.DESCRIPTION]
    prop_constraint = r[keys.SIMPLE_PROPERTY_CONSTRAINT]
    required = r[keys.REQUIRED]
    
    simple_prop = SimpleProp(label_str, simple_type, description_str, prop_constraint, required)

    return simple_prop
  
def create_list_prop(r):
    if r[keys.PROPERTY_TYPE] != resdef.LIST_TYPE_PROPERTY:
         raise SnapResDefError("The resdef type '%s' is not list property" % r[keys.PROPERTY_TYPE])
    label_str = r[keys.LABEL]
    description_str = r[keys.DESCRIPTION]
    min_size = r[keys.COMPOSITE_PROPERTY_MIN_SIZE]
    max_size = r[keys.COMPOSITE_PROPERTY_MAX_SIZE]
    # Figure out default type
    create_method = map_type_to_method[r[keys.COMPOSITE_PROPERTY_DEFAULT_ENTRY_TYPE][keys.PROPERTY_TYPE]]
    default_entry_type = create_method(r[keys.COMPOSITE_PROPERTY_DEFAULT_ENTRY_TYPE])
    required = r[keys.REQUIRED]
    
    list_prop = ListProp(label_str, default_entry_type, description_str, min_size, max_size, required)
    for entry in r[keys.COMPOSITE_PROPERTY_STRUCT]:
        create_method = map_type_to_method[entry[keys.PROPERTY_TYPE]]
        list_prop.append(create_method(entry))
    
    return list_prop
            
def create_dict_prop(r):
    if r[keys.PROPERTY_TYPE] != resdef.DICT_TYPE_PROPERTY:
         raise SnapResDefError("The resdef type '%s' is not dictionary property" % r[keys.PROPERTY_TYPE])
    label_str = r[keys.LABEL]
    description_str = r[keys.DESCRIPTION]
    min_size = r[keys.COMPOSITE_PROPERTY_MIN_SIZE]
    max_size = r[keys.COMPOSITE_PROPERTY_MAX_SIZE]
    keys_fixed = r[keys.FIXED_KEYS]
    required = r[keys.REQUIRED]
    # Figure out default type
    create_method = map_type_to_method[r[keys.COMPOSITE_PROPERTY_DEFAULT_ENTRY_TYPE][keys.PROPERTY_TYPE]]
    default_entry_type = create_method(r[keys.COMPOSITE_PROPERTY_DEFAULT_ENTRY_TYPE])
    
    dict_prop = DictProp(label_str, default_entry_type, description_str, min_size, max_size, keys_fixed, required)
    composite_s = r[keys.COMPOSITE_PROPERTY_STRUCT]
    for k in composite_s:
        create_method = map_type_to_method[composite_s[k][keys.PROPERTY_TYPE]]
        dict_prop[k] = create_method(composite_s[k])
    return dict_prop

map_type_to_method = dict.fromkeys(resdef.list_of_simple_types, create_simple_prop)
map_type_to_method[resdef.LIST_TYPE_PROPERTY] = create_list_prop
map_type_to_method[resdef.DICT_TYPE_PROPERTY] = create_dict_prop

def create_property_from_resdef(r):
    create_method = map_type_to_method[r[keys.PROPERTY_TYPE]]
    return create_method(r)

def substitute_params_in_resdef(res_def, params):
    """
    Takes param values and applies them to values of properties
    
    @param res_def: The resource definition that needs param substitution
    @type res_def:  ResDef
    
    @param params: Dictionary of param names and their values.
    @type params:  dict
    
    """
                
    #
    # Figure out the values of the properties
    #
    if params is not None and len(params) > 0:
        for name in res_def.list_property_names():
            value = res_def.get_property_value(name)
            d = res_def.get_property_def(name)
            prop_def = create_property_from_resdef(d[keys.PROPERTY_DEFINITION])
            value = substitute_params_in_property(value, params, prop_def)
            # Now we look for substituted properties
            res_def.set_property_value(name, value)

def substitute_params_in_property(value, params, prop_def):
    if (isinstance(prop_def, SimpleProp)):
        if not resdef.is_str(value):
            # This is not a string value, there is nothing to substitute here.
            return value
        vs = resdef.param_pattern.findall(value)
        if len(vs) > 0:
            not_substituted = None
            # First do the substituition.
            for v in vs:
                p = v[3:-1]
                t = params.get(p, None)
                if t is None:
                    # A param value was not specified for p.
                    not_substituted = p
                else:
                    value = value.replace(v,t)
            # Next, convert the "fully" substituted value to expected type (like decimal, bool or detettime.
            if not_substituted is None:
                if prop_def.simple_type == "number":
                    try:
                        value = Decimal(value)
                    except Exception:
                        raise SnapException("Invalid param-substituted value '%s' for property '%s'" %
                                             (value, prop_def.label_str))
                elif prop_def.simple_type == "boolean":
                    b = value.lower()
                    if b == "true":
                        value = True
                    elif b == "false":
                        value = False
                    else:
                        raise SnapException("Invalid param-substituted value '%s' for property '%s'" %
                                             (value, prop_def.label_str))
                elif prop_def.simple_type == "datetime":
                    # We do not support datetime as yet.
                    raise SnapException("Datetime property '%s' cannot be parameterized." % prop_def.label_str)
                err_str = prop_def.validate(value)
                if err_str is not None:
                    raise SnapException("Property '%s' had validation error - %s" % (prop_def.label_str, err_str))
                
    elif isinstance(prop_def, ListProp):
        if value is not None:
            if (not hasattr(value, "append")) or (not callable(value.append)):
                raise SnapException("Value of property '%s' was not a list. Received: %s" % 
                                    (prop_def.label_str, value))
            for (i, entry) in enumerate(value):
                value[i] = substitute_params_in_property(entry, params, prop_def.get_item_type(i))
    elif isinstance(prop_def, DictProp):
        if value is not None:
            if (not hasattr(value, "keys")) or (not callable(value.keys)):
                raise SnapException("Value of property '%s' value was not a dictionary. Received: %s" % 
                                    (prop_def.label_str, value))
            for k in value:
                value[k] = substitute_params_in_property(value[k], params, prop_def.get_item_type(k))
    
    return value
    
def validate_param_notations(param_names, prop_values, prop_def_dict, prop_err_obj):
    """
    Validate that every param substitution notation (i.e. $?{}) in resdef refers to a defined param name.
    
    @param param_names: Names of params defined for this resdef.
    @type param_names:  list
        
    @param prop_def_dict: The dictionary of property name -> property definition dictionary
    @type prop_def_dict:  dict
    
    @param prop_values: The dictionary of property name -> property values
    @type prop_values:  dict
    
    @param prop_err_obj: Error object for  the resdef being validated.
    @type prop_err_obj:  Error object.
    
    """
    for name in prop_values:
        prop_def = create_property_from_resdef(prop_def_dict[name][keys.PROPERTY_DEFINITION])
        validate_param_notations_in_prop(param_names, prop_values[name], prop_def, prop_err_obj[name])

def validate_param_notations_in_prop(param_names, value, prop_def, err_obj):
    """
    Validate that every param substitution notation (i.e. $?{}) in a property refers to a defined param name.
    
    @param param_names: Names of params defined for this resdef.
    @type param_names:  list
    
    @param value: The property value being inspected.
    @type value:  Any valid property value.
    
    @param prop_def: The property definition corresponding to the value.
    @type prop_def:  L{SimpleProp}, L{ListProp}, L{DictProp}
    
    @param err_obj: Error object for the property being validated. This object will be set with error
        messages, if validation fails.
    @type err_obj: Error object.
    
    """
    if isinstance(prop_def, SimpleProp) and resdef.is_str(value):
        vs = resdef.param_pattern.findall(value)
        not_found = None
        for v in vs:
            p = v[3:-1]
            if p not in param_names:
                if not_found is None:
                    not_found = []
                not_found.append(p)
        
        if not_found:
            err_obj.set_message("Resource definition has no parameter(s) named - %s" % ", ".join(not_found))
                
    elif isinstance(prop_def, ListProp):
        if value is not None:
            if (not hasattr(value, "append")) or (not callable(value.append)):
                # There are other issues with this value, don't dive down this branch of the tree.
                return
            for (i, entry) in enumerate(value):
                if i >= len (err_obj):
                    # The index overshoots limits. Let the other validation code report it.
                    continue
                validate_param_notations_in_prop(param_names, entry, prop_def.get_item_type(i), err_obj[i])
    elif isinstance(prop_def, DictProp):
        if value is not None:
            if (not hasattr(value, "keys")) or (not callable(value.keys)):
                # There are other issues with this value, don't dive down this branch of the tree.
                return
            for k in value:
                if k not in err_obj:
                    # This must be an invalid key. Let the other validation code report it.
                    continue
                validate_param_notations_in_prop(param_names, value[k], prop_def.get_item_type(k), err_obj[k])
                
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.