import autosar.component
import autosar.behavior
import autosar.element
import autosar.portinterface
import autosar.datatype
import autosar.mode
import autosar.builder
import copy
import autosar.base
import re
from fractions import Fraction
import collections
import decimal
import sys

_supress_warnings = True

class Package(object):
    packageName = None
    def __init__(self, name, parent=None, role=None):
        self.name = name
        self.elements = []
        self.subPackages = []
        self.parent=parent
        self.role=role
        self.map={'elements':{}, 'packages':{}}
        self.unhandledParser = set() #[PackageParser] unhandled
        self.unhandledWriter =set() #[PackageWriter] Unhandled

    def __getitem__(self,key):
        if isinstance(key,str):
            return self.find(key)
        else:
            raise ValueError('expected string')

    @property
    def ref(self):
        if self.parent is not None:
            return self.parent.ref+'/%s'%self.name
        else:
            return ''

    def find(self,ref):
        if ref.startswith('/'): return self.parent.find(ref)
        ref = ref.partition('/')
        name = ref[0]
        if name in self.map['packages']:
            package=self.map['packages'][name]
            if len(ref[2])>0:
                return package.find(ref[2])
            else:
                return package
        if name in self.map['elements']:
            elem=self.map['elements'][name]
            if len(ref[2])>0:
                return elem.find(ref[2])
            else:
                return elem
        return None

    def findall(self,ref):
        """
        experimental find-method that has some rudimentary support for globs.
        """
        if ref is None: return None
        if ref[0]=='/': ref=ref[1:] #removes initial '/' if it exists
        ref = ref.partition('/')
        if ref[0]=='*' and len(ref[2])==0:
            result=list(self.elements)
            result.extend(self.subPackages)
        else:
            result=[]
            for item in (self.elements+self.subPackages):
                if item.name == ref[0] or ref[0]=='*':
                    if len(ref[2])>0:
                        result.extend(item.findall(ref[2]))
                    else:
                        result.append(item)
            if (len(result)==0) and ('*' in ref[0]):
                p = re.compile(ref[0].replace('*','.*'))
                for item in (self.elements+self.subPackages):
                    m = p.match(item.name)
                    if m is not None:
                        if len(ref[2])>0:
                            result.extend(item.findall(ref[2]))
                        else:
                            result.append(item)
        return result

    def dir(self,ref=None,_prefix=''):
        if ref==None:
            return [_prefix+x.name for x in self.subPackages]+[_prefix+x.name for x in self.elements]
        else:
            ref = ref.partition('/')
            result=self.find(ref[0])
            if result is not None:
                return result.dir(ref[2] if len(ref[2])>0 else None,_prefix+ref[0]+'/')
            else:
                return None

    def delete(self, ref):
        if ref is None: return
        if ref[0]=='/': ref=ref[1:] #removes initial '/' if it exists
        ref = ref.partition('/')
        for i,element in enumerate(self.elements):
            if element.name == ref[0]:
                if len(ref[2])>0:
                    return element.delete(ref[2])
                else:
                    del self.elements[i]
                    del self.map['elements'][ref[0]]
                    break

    def createSenderReceiverInterface(self, name, dataElements=None, modeGroups=None, isService=False, serviceKind = None, adminData=None):
        """
        creates a new sender-receiver port interface. dataElements can either be a single instance of DataElement or a list of DataElements.
        The same applies to modeGroups. isService must be boolean
        """

        ws = self.rootWS()
        assert(ws is not None)

        portInterface = autosar.portinterface.SenderReceiverInterface(str(name), isService, adminData=adminData)
        if dataElements is not None:
            if isinstance(dataElements,collections.abc.Iterable):
                for elem in dataElements:
                    dataType=ws.find(elem.typeRef, role='DataType')
                    if dataType is None:
                        raise ValueError('invalid type reference: '+elem.typeRef)
                    elem.typeRef=dataType.ref #normalize reference to data element
                    portInterface.append(elem)
            elif isinstance(dataElements,autosar.portinterface.DataElement):
                dataType=ws.find(dataElements.typeRef, role='DataType')
                if dataType is None:
                    raise ValueError('invalid type reference: '+dataElements.typeRef)
                dataElements.typeRef=dataType.ref #normalize reference to data element
                portInterface.append(dataElements)
            else:
                raise ValueError("dataElements: expected autosar.portinterface.DataElement instance or list")
        if modeGroups is not None:
            if isinstance(modeGroups,collections.abc.Iterable):
                for elem in modeGroups:
                    portInterface.append(elem)
            elif isinstance(modeGroups,autosar.mode.ModeGroup):
                portInterface.append(modeGroups)
            else:
                raise ValueError("dataElements: expected autosar.portinterface.DataElement instance or list")
        self.append(portInterface)
        return portInterface

    def createParameterInterface(self, name, parameters=None, modeDeclarationGroups=None, isService=False, adminData=None):
        """
        Creates a new parameter port interface. parameter can either be a single instance of Parameter or a list of Parameters.
        The same applies to modeDeclarationGroups. isService must be boolean.
        In a previous version of this function the class DataElement was used instead of Parameter.
        In order to be backward compatible with old code, this method converts from old to new datatype internally
        """
        ws = self.rootWS()
        assert(ws is not None)

        if isinstance(adminData, dict):
            adminDataObj=ws.createAdminData(adminData)
        else:
            adminDataObj = adminData
        if (adminDataObj is not None) and not isinstance(adminDataObj, autosar.base.AdminData):
            raise ValueError("adminData must be of type dict or AdminData")
        portInterface = autosar.portinterface.ParameterInterface(str(name), adminData=adminDataObj)
        if parameters is not None:
            if isinstance(parameters,collections.abc.Iterable):
                for elem in parameters:
                    dataType=ws.find(elem.typeRef, role='DataType')
                    #normalize reference to data element
                    if dataType is None:
                        raise ValueError('invalid type reference: '+elem.typeRef)
                    elem.typeRef=dataType.ref
                    if isinstance(autosar.portinterface.DataElement):
                        #convert into Parameter
                        parameter = autosar.element.ParameterDataPrototype(elem.name, elem.typeRef, elem.swAddressMethodRef, adminData=elem.adminData)
                    else:
                        parameter = elem
                    portInterface.append(parameter)
            elif isinstance(parameters, autosar.portinterface.DataElement):
                dataType=ws.find(parameters.typeRef, role='DataType')
                #normalize reference to data element
                if dataType is None:
                    raise ValueError('invalid type reference: '+parameters.typeRef)
                parameters.typeRef=dataType.ref
                parameter = autosar.element.ParameterDataPrototype(parameters.name, parameters.typeRef,
                                                            parameters.swAddressMethodRef, adminData=parameters.adminData)
                portInterface.append(parameter)
            elif isinstance(parameters, autosar.element.ParameterDataPrototype):
                dataType=ws.find(parameters.typeRef, role='DataType')
                #normalize reference to data element
                if dataType is None:
                    raise ValueError('invalid type reference: '+parameters.typeRef)
                parameters.typeRef=dataType.ref
                portInterface.append(parameters)
            else:
                raise ValueError("parameters: Expected instance of autosar.element.ParameterDataPrototype or list")
        self.append(portInterface)
        return portInterface

    def createModeSwitchInterface(self, name, modeGroup = None, isService=False, adminData=None):
        portInterface = autosar.portinterface.ModeSwitchInterface(name, isService, self, adminData)
        if modeGroup is not None:
            if isinstance(modeGroup, autosar.mode.ModeGroup):
                ws = self.rootWS()
                assert (ws is not None)
                modeDeclarationGroup = ws.find(modeGroup.typeRef, role='ModeDclrGroup')
                if modeDeclarationGroup is None:
                    raise ValueError('invalid type reference: '+modeGroup.typeRef)
                modeGroup.typeRef=modeDeclarationGroup.ref #normalize reference string
                portInterface.modeGroup = modeGroup
                modeGroup.parent = portInterface
            else:
                raise ValueError('modeGroup must be an instance of autosar.mode.ModeGroup or None')
        self.append(portInterface)
        return portInterface

    def createNvDataInterface(self, name, nvDatas=None, isService=False, serviceKind = None, adminData=None):
        """
        creates a new nv-data port interface. nvDatas can either be a single instance of DataElement or a list of DataElements.
        isService must be boolean
        """

        ws = self.rootWS()
        assert(ws is not None)

        portInterface = autosar.portinterface.NvDataInterface(str(name), isService=isService, serviceKind=serviceKind, adminData=adminData)
        if nvDatas is not None:
            if isinstance(nvDatas,collections.abc.Iterable):
                for elem in nvDatas:
                    dataType=ws.find(elem.typeRef, role='DataType')
                    if dataType is None:
                        raise ValueError('invalid type reference: '+elem.typeRef)
                    elem.typeRef=dataType.ref #normalize reference to data element
                    portInterface.append(elem)
            elif isinstance(nvDatas,autosar.portinterface.DataElement):
                dataType=ws.find(nvDatas.typeRef, role='DataType')
                if dataType is None:
                    raise ValueError('invalid type reference: '+nvDatas.typeRef)
                nvDatas.typeRef=dataType.ref #normalize reference to data element
                portInterface.append(nvDatas)
            else:
                raise ValueError("dataElements: expected autosar.portinterface.DataElement instance or list")
        self.append(portInterface)
        return portInterface

    def createSubPackage(self, name, role=None):
        pkg = Package(name)
        self.append(pkg)
        if role is not None:
            ws = self.rootWS()
            assert(ws is not None)
            ws.setRole(pkg.ref, role)
        return pkg

    def rootWS(self):
        if self.parent is None:
            return None
        else:
            return self.parent.rootWS()

    def append(self,elem):
        """appends elem to the self.elements list"""
        isNewElement = True
        if elem.name in self.map['elements']:
            isNewElement = False
            existingElem = self.map['elements'][elem.name]
            if type(elem) != type(existingElem):
                raise TypeError('Error: element %s %s already exists in package %s with different type from new element %s'%(str(type(existingElem)), existingElem.name, self.name, str(type(elem))))
            else:
                if elem != existingElem:
                    raise ValueError('Error: element %s %s already exist in package %s using different definition'%(existingElem.name, str(type(existingElem)), self.name))
        if isNewElement:
            if isinstance(elem,autosar.element.Element):
                self.elements.append(elem)
                elem.parent=self
                self.map['elements'][elem.name]=elem
            elif isinstance(elem,Package):
                self.subPackages.append(elem)
                elem.parent=self
                self.map['packages'][elem.name]=elem
            else:
                raise ValueError('unexpected value type %s'%str(type(elem)))

    def update(self,other):
        """copies/clones each element from other into self.elements"""
        if type(self) == type(other):
            for otherElem in other.elements:
                newElem=copy.deepcopy(otherElem)
                assert(newElem is not None)
                try:
                    i=self.index('elements',otherElem.name)
                    oldElem=self.elements[i]
                    self.elements[i]=newElem
                    oldElem.parent=None
                except ValueError:
                    self.elements.append(newElem)
                newElem.parent=self
        else:
            raise ValueError('cannot update from object of different type')

    def index(self,container,name):
        if container=='elements':
            lst=self.elements
        elif container=='subPackages':
            lst=self.subPackages
        else:
            raise KeyError("%s not in %s"%(container,self.__class__.__name__))
        return autosar.base.indexByName(lst,name)


    def createApplicationSoftwareComponent(self, swcName, behaviorName=None, implementationName=None, multipleInstance=False, autoCreatePortAPIOptions=True):
        """
        Creates a new ApplicationSoftwareComponent object and adds it to the package.
        It also creates an InternalBehavior object as well as an SwcImplementation object.

        """
        ws = self.rootWS()
        assert(ws is not None)
        swc = autosar.component.ApplicationSoftwareComponent(swcName,self)
        self.append(swc)
        self._createInternalBehavior(ws, swc, behaviorName, multipleInstance, autoCreatePortAPIOptions)
        self._createImplementation(swc, implementationName)
        return swc

    def createServiceComponent(self, swcName, behaviorName=None, implementationName=None, multipleInstance=False, autoCreatePortAPIOptions=True):
        """
        Creates a new ApplicationSoftwareComponent object and adds it to the package.
        It also creates an InternalBehavior object as well as an SwcImplementation object.
        """
        ws = self.rootWS()
        assert(ws is not None)

        swc = autosar.component.ServiceComponent(swcName,self)
        self.append(swc)
        self._createInternalBehavior(ws, swc, behaviorName, multipleInstance, autoCreatePortAPIOptions)
        self._createImplementation(swc, implementationName)
        return swc

    def createComplexDeviceDriverComponent(self,swcName,behaviorName=None,implementationName=None,multipleInstance=False, autoCreatePortAPIOptions=True):
        ws = self.rootWS()
        assert(ws is not None)
        swc=autosar.component.ComplexDeviceDriverComponent(swcName, parent=self)
        self.append(swc)
        self._createInternalBehavior(ws, swc, behaviorName, multipleInstance, autoCreatePortAPIOptions)
        self._createImplementation(swc, implementationName)
        return swc

    def createNvBlockComponent(self,swcName,behaviorName=None,implementationName=None,multipleInstance=False, autoCreatePortAPIOptions=False):
        """
        Creates a new NvBlockComponent object and adds it to the package.
        It also creates an InternalBehavior object as well as an SwcImplementation object.

        """
        ws = self.rootWS()
        assert(ws is not None)
        swc = autosar.component.NvBlockComponent(swcName,self)
        self.append(swc)
        self._createInternalBehavior(ws, swc, behaviorName, multipleInstance, autoCreatePortAPIOptions)
        self._createImplementation(swc, implementationName)
        return swc

    def createCompositionComponent(self, componentName, adminData=None):
        component = autosar.component.CompositionComponent(str(componentName), self)
        self.append(component)
        return component

    def _createInternalBehavior(self, ws, swc, behaviorName, multipleInstance, autoCreatePortAPIOptions):
        """
        Initializes swc.behavior object
        For AUTOSAR3, an instance of InternalBehavior is created
        For AUTOSAR4, an instance of SwcInternalBehavior is created
        """
        if behaviorName is None:
            behaviorName = swc.name+'_InternalBehavior'
        if ws.version < 4.0:
            # In AUTOSAR 3.x the internal behavior is a sub-element of the package.
            internalBehavior = autosar.behavior.InternalBehavior(behaviorName,swc.ref,multipleInstance,self)
        else:
            # In AUTOSAR 4.x the internal behavior is a sub-element of the swc.
            internalBehavior = autosar.behavior.SwcInternalBehavior(behaviorName,swc.ref,multipleInstance, swc)
        internalBehavior.autoCreatePortAPIOptions=autoCreatePortAPIOptions
        swc.behavior=internalBehavior
        if ws.version < 4.0:
            # In AUTOSAR 3.x the internal behavior is a sub-element of the package.
            self.append(internalBehavior)

    def _createImplementation(self, swc, implementationName):

        if implementationName is None:
            implementationName = swc.name+'_Implementation'
        swc.implementation = autosar.component.SwcImplementation(implementationName, swc.behavior.ref, parent=self)

        self.append(swc.implementation)


    def createModeDeclarationGroup(self, name, modeDeclarations = None, initialMode = None, category=None, adminData=None):
        """
        creates an instance of autosar.portinterface.ModeDeclarationGroup
        name: name of the ModeDeclarationGroup
        modeDeclarations: list of strings where each string is a mode name. It can also be a list of tuples of type (int, str).
        initialMode: string with name of the initial mode (must be one of the strings in modeDeclarations list)
        category: optional category string
        adminData: optional adminData object (use ws.createAdminData() as constructor)
        """
        ws = self.rootWS()
        assert(ws is not None)
        if isinstance(adminData, dict):
            adminDataObj=ws.createAdminData(adminData)
        else:
            adminDataObj = adminData
        if (adminDataObj is not None) and not isinstance(adminDataObj, autosar.base.AdminData):
            raise ValueError("adminData must be of type dict or AdminData")
        group = autosar.mode.ModeDeclarationGroup(name, None, None, category, self,adminDataObj)
        if modeDeclarations is not None:
            for declaration in modeDeclarations:
                if isinstance(declaration, str):
                    declarationName = declaration
                    item=autosar.mode.ModeDeclaration(declarationName, parent = group)
                elif isinstance(declaration, tuple):
                    declarationValue = declaration[0]
                    declarationName = declaration[1]
                    assert(isinstance(declarationValue, int))
                    assert(isinstance(declarationName, str))
                    item=autosar.mode.ModeDeclaration(declarationName, declarationValue, group)
                else:
                    raise NotImplementedError(type(declaration))
                group.modeDeclarations.append(item)
                if (initialMode is not None) and (declarationName == initialMode):
                    group.initialModeRef = item.ref
            if (initialMode is not None) and (group.initialModeRef is None):
                raise ValueError('initalMode "%s" not a valid modeDeclaration name'%initialMode)
            self.append(group)
        return group

    def createClientServerInterface(self, name, operations, errors=None, isService=False, serviceKind=None, adminData=None):
        """
        creates a new client server interface in current package
        name: name of the interface (string)
        operations: names of the operations in the interface (list of strings)
        errors: Possible errors that can be returned. Can be a single instance of ApplicationError or a list of ApplicationError
        isService: True if this interface is a service interface (bool)
        adminData: optional admindata (dict or autosar.base.AdminData object)
        """
        portInterface = autosar.portinterface.ClientServerInterface(name, isService, serviceKind, self, adminData)
        for name in operations:
            portInterface.append(autosar.portinterface.Operation(name))
        if errors is not None:
            if isinstance(errors, collections.abc.Iterable):
                for error in errors:
                    portInterface.append(error)
            else:
                assert( isinstance(errors, autosar.portinterface.ApplicationError))
                portInterface.append(errors)
        self.append(portInterface)
        return portInterface

    def createSoftwareAddressMethod(self, name):
        item = autosar.element.SoftwareAddressMethod(name)
        self.append(item)
        return item

    def createArrayDataType(self, name, typeRef, arraySize, elementName=None, adminData=None):
        """
        AUTOSAR3:
           Creates an ArrayDataType and adds it to current package
        """
        ws = self.rootWS()
        assert(ws is not None)
        if ws.version >= 4.0:
            raise RuntimeError("This method is only valid in AUTOSAR3")
        else:
            if typeRef.startswith('/'):
                dataType = ws.find(typeRef)
            else:
                dataType = ws.find(typeRef, role='DataType')
            if dataType is None:
                raise autosar.base.InvalidDataTypeRef(typeRef)
            newType = autosar.datatype.ArrayDataType(name, typeRef, arraySize, adminData)
            self.append(newType)
            return newType

    def createIntegerDataType(self, name, min=None, max=None, valueTable=None, offset=None, scaling=None, unit=None, baseTypeRef=None, adminData=None, swCalibrationAccess='NOT-ACCESSIBLE', typeEmitter=None, forceFloatScaling=False, dataConstraint=''):
        """
        AUTOSAR3:
        Helper method for creating integer datatypes in a package.
        In order to use this function you must have a subpackage present with role='CompuMethod'.

        AUTOSAR4:
        Helper method for creating implementation datatypes or application datatypes.
        """
        ws=self.rootWS()
        assert(ws is not None)
        if ws.version >= 4.0:
            raise RuntimeError("This method is only valid in AUTOSAR3")
        else:
            compuMethodRef = self._createCompuMethodAndUnitV3(ws, name, min, max, valueTable, None, offset, scaling, unit, forceFloatScaling)
            lowerLimit, upperLimit = min, max
            if compuMethodRef is not None:
                compuMethod = ws.find(compuMethodRef)
                if lowerLimit is None:
                    lowerLimit = compuMethod.intToPhys.lowerLimit
                if upperLimit is None:
                    upperLimit = compuMethod.intToPhys.upperLimit
            newType=autosar.datatype.IntegerDataType(name, lowerLimit, upperLimit, compuMethodRef=compuMethodRef, adminData=adminData)
            assert(newType is not None)
            self.append(newType)
            return newType

    def createRealDataType(self, name, minVal, maxVal, minValType='CLOSED', maxValType='CLOSED', hasNaN=False, encoding='SINGLE', baseTypeRef=None, typeEmitter=None, adminData=None):
        """
        AUTOSAR 4: Creates a new ImplementationDataType
        AUTOSAR 3: Creates a new instance of autosar.datatype.RealDataType and appends it to current package
        """
        ws=self.rootWS()
        assert(ws is not None)
        if ws.version >= 4.0:
            if baseTypeRef is None:
                raise ValueError('baseTypeRef argument must be given to this method')
            if (minVal == '-INFINITE' or minVal == 'INFINITE'): minVal = '-INF'
            if (maxVal == 'INFINITE'): maxVal = 'INF'
            if minVal == '-INF' and minValType == 'CLOSED':
                minValType = 'OPEN' #automatic correction
            if maxVal == 'INF' and maxValType == 'CLOSED':
                maxValType = 'OPEN' #automatic correction
            dataConstraint = self.createInternalDataConstraint(name+'_DataConstr', minVal, maxVal, minValType, maxValType)
            newType = autosar.datatype.ImplementationDataType(name, 'VALUE', typeEmitter=typeEmitter)
            props = autosar.base.SwDataDefPropsConditional(baseTypeRef=baseTypeRef,
                                                               swCalibrationAccess='NOT-ACCESSIBLE',
                                                               dataConstraintRef=dataConstraint.ref)
            newType.variantProps = [props]
        else:
            if ( (minVal == '-INFINITE') or (minVal == '-INF') or (minVal == 'INFINITE') or (minVal == 'INF') ):
                #automatic correction
                minVal = None
                minValType = 'INFINITE'
            if ( (maxVal == 'INF') or (maxVal == 'INFINITE') ):
                #automatic correction
                maxVal = None
                maxValType = 'INFINITE'
            newType=autosar.datatype.RealDataType(name, minVal, maxVal, minValType, maxValType, hasNaN, encoding, self, adminData)
        self.append(newType)
        return newType

    def createRecordDataType(self, name, elements, adminData=None):
        """
        AUTOSAR3: Create a new instance of RecordDataType and appends it to current package
        """
        ws = self.rootWS()
        assert(ws is not None)
        if ws.version >= 4.0:
            raise RuntimeError("This method is only for AUTOSAR3")
        else:
            processed = []
            for elem in elements:
                elemUnitRef = None
                if isinstance(elem, autosar.datatype.RecordTypeElement):
                    processed.append(elem)
                elif isinstance(elem, tuple):
                    elemName = elem[0]
                    elemUnitRef = elem[1]
                elif isinstance(elem, collections.abc.Mapping):
                    elemName = elem['name']
                    elemUnitRef = elem['typeRef']
                else:
                    raise ValueError('element must be either Mapping, RecordTypeElement or tuple')
                if elemUnitRef is not None:
                    if elemUnitRef.startswith('/'):
                        dataType = ws.find(elemUnitRef)
                    else:
                        dataType = ws.find(elemUnitRef, role='DataType')
                    if dataType is None:
                        raise autosar.base.InvalidDataTypeRef(elemUnitRef)
                    elem = autosar.datatype.RecordTypeElement(elemName, dataType.ref)
                    processed.append(elem)
            dataType = autosar.datatype.RecordDataType(name, processed, self, adminData)
            self.append(dataType)
            return dataType

    def createStringDataType(self, name, length, encoding='ISO-8859-1', adminData=None):
        """
        create a new instance of autosar.datatype.StringDataType and appends it to current package
        """
        dataType=autosar.datatype.StringDataType(name, length, encoding, self, adminData)
        self.append(dataType)
        return dataType

    def createBooleanDataType(self, name, adminData=None):
        """
        create a new instance of autosar.datatype.BooleanDataType and appends it to current package
        """
        dataType=autosar.datatype.BooleanDataType(name, self, adminData)
        self.append(dataType)
        return dataType

    def createConstant(self, name, typeRef, initValue, label = '', adminData=None):
        """
        create a new instance of autosar.constant.Constant and appends it to the current package
        """
        ws=self.rootWS()
        assert(ws is not None)
        if typeRef is not None:
            dataType = ws.find(typeRef, role='DataType')
            if dataType is None:
                raise autosar.base.InvalidDataTypeRef(str(typeRef))
        else:
            if ws.version < 4.0:
                raise ValueError('typeRef argument cannot be None')
            else:
                dataType = None
        if ws.version < 4.0:
            return self._createConstantV3(ws, name, dataType, initValue, adminData)
        else:
            return self._createConstantV4(ws, name, dataType, initValue, label, adminData)

    def _createConstantV3(self, ws, name, dataType, initValue, adminData=None):
        """Creates an AUTOSAR 3 Constant"""
        if isinstance(dataType, autosar.datatype.IntegerDataType):
            if not isinstance(initValue, int):
                raise ValueError('initValue: expected type int, got '+str(type(initValue)))
            value=autosar.constant.IntegerValue(name, dataType.ref, initValue)
        elif isinstance(dataType, autosar.datatype.RecordDataType):
            if isinstance(initValue, collections.abc.Mapping) or isinstance(initValue, collections.abc.Iterable):
                pass
            else:
                raise ValueError('initValue: expected type Mapping or Iterable, got '+str(type(initValue)))
            value=self._createRecordValueV3(ws, name, dataType, initValue)
        elif isinstance(dataType, autosar.datatype.ArrayDataType):
            if isinstance(initValue, collections.abc.Iterable):
                pass
            else:
                raise ValueError('initValue: expected type Iterable, got '+str(type(initValue)))
            value=self._createArrayValueV3(ws, name, dataType, initValue)
        elif isinstance(dataType, autosar.datatype.BooleanDataType):
            if isinstance(initValue, bool):
                pass
            elif isinstance(initValue, str) or isinstance(initValue, int):
                initValue=bool(initValue)
            else:
                raise ValueError('initValue: expected type bool or str, got '+str(type(initValue)))
            value=autosar.constant.BooleanValue(name, dataType.ref, initValue)
        elif isinstance(dataType, autosar.datatype.StringDataType):
            if isinstance(initValue, str):
                pass
            else:
                raise ValueError('initValue: expected type str, got '+str(type(initValue)))
            value=autosar.constant.StringValue(name, dataType.ref, initValue)
        elif isinstance(dataType, autosar.datatype.RealDataType):
            if isinstance(initValue, float) or isinstance(initValue, decimal.Decimal) or isinstance(initValue, int):
                pass
            else:
                raise ValueError('initValue: expected type int, float or Decimal, got '+str(type(initValue)))
            raise NotImplementedError("Creating constants from RealDataType not implemented")
        else:
            raise ValueError('unrecognized type: '+str(type(dataType)))
        assert(value is not None)
        constant = autosar.constant.Constant(name, value, adminData=adminData)
        self.append(constant)
        return constant

    def _createRecordValueV3(self, ws, name, dataType, initValue, parent=None):
        value = autosar.constant.RecordValue(name, dataType.ref, parent = parent)
        if isinstance(initValue, collections.abc.Mapping):
            for elem in dataType.elements:
                if elem.name in initValue:
                    v = initValue[elem.name]
                    childType = ws.find(elem.typeRef, role='DataType')
                    if childType is None:
                        raise ValueError('invalid reference: '+str(elem.typeRef))
                    if isinstance(childType, autosar.datatype.IntegerDataType):
                        if not isinstance(v, int):
                            raise ValueError('v: expected type int, got '+str(type(v)))
                        value.elements.append(autosar.constant.IntegerValue(elem.name, childType.ref, v, value))
                    elif isinstance(childType, autosar.datatype.RecordDataType):
                        if isinstance(v, collections.abc.Mapping) or isinstance(v, collections.abc.Iterable):
                            pass
                        else:
                            raise ValueError('v: expected type Mapping or Iterable, got '+str(type(v)))
                        value.elements.append(self._createRecordValueV3(ws, elem.name, childType, v, value))
                    elif isinstance(childType, autosar.datatype.ArrayDataType):
                        if isinstance(v, collections.abc.Iterable):
                            pass
                        else:
                            raise ValueError('v: expected type Iterable, got '+str(type(v)))
                        value.elements.append(self._createArrayValueV3(ws, elem.name, childType, v, value))
                    elif isinstance(childType, autosar.datatype.BooleanDataType):
                        if isinstance(v, bool):
                            pass
                        elif isinstance(v, str) or isinstance(v, int):
                            v=bool(v)
                        else:
                            raise ValueError('v: expected type bool or str, got '+str(type(v)))
                        value.elements.append(autosar.constant.BooleanValue(elem.name, childType.ref, v, value))
                    elif isinstance(childType, autosar.datatype.StringDataType):
                        if isinstance(v, str):
                            pass
                        else:
                            raise ValueError('v: expected type str, got '+str(type(v)))
                        value.elements.append(autosar.constant.StringValue(elem.name, childType.ref, v, value))
                    elif isinstance(childType, autosar.datatype.RealDataType):
                        if isinstance(v, float) or isinstance(v, decimal.Decimal) or isinstance(v, int):
                            pass
                        else:
                            raise ValueError('v: expected type int, float or Decimal, got '+str(type(v)))
                        raise NotImplementedError("Creating constants from RealDataType not implemented")
                    else:
                        raise ValueError('unrecognized type: '+str(type(childType)))
                else:
                    raise ValueError('%s: missing initValue field: %s'%(name, elem.name))
        else:
            raise NotImplementedError(type(initValue))
        return value


    def _createArrayValueV3(self, ws, name, dataType, initValue, parent=None):
        value = autosar.constant.ArrayValue(name, dataType.ref, parent=parent)
        childType = ws.find(dataType.typeRef, role='DataType')
        if childType is None:
            raise ValueError('invalid reference: '+str(dataType.typeRef))
        if isinstance(initValue, collections.abc.Iterable):
            for i in range(dataType.length):
                try:
                    v=initValue[i]
                except IndexError:
                    raise ValueError('%s: too few elements in initValue, expected %d items, got %d'%(name, int(dataType.length), len(initValue)))
                elemName='%s_%d'%(childType.name,i)
                if isinstance(childType, autosar.datatype.IntegerDataType):
                    if not isinstance(v, int):
                        raise ValueError('v: expected type int, got '+str(type(v)))
                    value.elements.append(autosar.constant.IntegerValue(elemName, childType.ref, v, value))
                elif isinstance(childType, autosar.datatype.RecordDataType):
                    if isinstance(v, collections.abc.Mapping) or isinstance(v, collections.abc.Iterable):
                        value.elements.append(self._createRecordValueV3(ws, elemName, childType, v, value))
                    else:
                        raise ValueError('v: expected type Mapping or Iterable, got '+str(type(v)))
                elif isinstance(childType, autosar.datatype.ArrayDataType):
                    if isinstance(v, collections.abc.Iterable):
                        pass
                    else:
                        raise ValueError('v: expected type Iterable, got '+str(type(v)))
                    value.elements.append(self._createArrayValueV3(ws, elemName, childType, v, value))
                elif isinstance(childType, autosar.datatype.BooleanDataType):
                    if isinstance(v, bool):
                        pass
                    elif isinstance(v, str) or isinstance(v, int):
                        v=bool(v)
                    else:
                        raise ValueError('v: expected type bool or str, got '+str(type(v)))
                    value.elements.append(autosar.constant.BooleanValue(elemName, childType.ref, v, value))
                elif isinstance(childType, autosar.datatype.StringDataType):
                    if isinstance(v, str):
                        pass
                    else:
                        raise ValueError('v: expected type str, got '+str(type(v)))
                    value.elements.append(autosar.constant.StringValue(elemName, childType.ref, v, value))
                elif isinstance(childType, autosar.datatype.RealDataType):
                    if isinstance(v, float) or isinstance(v, decimal.Decimal) or isinstance(v, int):
                        pass
                    else:
                        raise ValueError('v: expected type int, float or Decimal, got '+str(type(v)))
                    raise NotImplementedError("Creating constants from RealDataType not implemented")
                else:
                    raise ValueError('unrecognized type: '+str(type(childType)))
        else:
            raise NotImplementedError(type(initValue))
        return value

    def _createConstantV4(self, ws, name, dataType, initValue, label, adminData=None):
        """
        If label argument is an empty string, the label of the created value is the same as
        the name of the constant.
        """
        if isinstance(label, str) and len(label) == 0:
            label = name
        builder = autosar.builder.ValueBuilder()
        if dataType is None:
            value = builder.build(label, initValue)
        else:
            value = builder.buildFromDataType(dataType, initValue, label, ws)
        assert(value is not None)
        constant = autosar.constant.Constant(name, value, parent=self, adminData=adminData)
        self.append(constant)
        return constant


    def createTextValueConstant(self, name, value):
        """AUTOSAR 4 text value constant"""
        constant = autosar.constant.Constant(name, None, self)
        constant.value = autosar.constant.TextValue(name, value, constant)
        self.append(constant)
        return constant

    def createNumericalValueConstant(self, name, value):
        """AUTOSAR 4 numerical value constant"""
        constant = autosar.constant.Constant(name, None, self)
        constant.value = autosar.constant.NumericalValue(name, value, constant)
        self.append(constant)
        return constant

    def createApplicationValueConstant(self, name, swValueCont = None, swAxisCont = None, valueCategory = None, valueLabel = None):
        """
        (AUTOSAR4)
        Creates a new Constant containing a application value specification
        """
        ws=self.rootWS()
        assert(ws is not None)
        constant = autosar.constant.Constant(name, None, self)
        innerValue = autosar.constant.ApplicationValue(valueLabel, swValueCont, swAxisCont, valueCategory, parent = self)
        constant.value = innerValue
        self.append(constant)
        return constant

    def createConstantFromValue(self, name, value):
        """
        (AUTOSAR4)
        Wraps an already created value in a constant object
        """
        if not isinstance(value, autosar.constant.ValueAR4):
            raise ValueError("value argument must inherit from class ValueAR4 (got {})".format(type(value)))
        ws=self.rootWS()
        assert(ws is not None)
        constant = autosar.constant.Constant(name, value, self)
        self.append(constant)
        return constant



    def createInternalDataConstraint(self, name, lowerLimit, upperLimit, lowerLimitType="CLOSED", upperLimitType="CLOSED", adminData = None):
        ws=self.rootWS()
        assert(ws is not None)
        return self._checkAndCreateDataConstraint(ws, name, 'internalConstraint', lowerLimit, upperLimit, lowerLimitType, upperLimitType, adminData)

    def createPhysicalDataConstraint(self, name, lowerLimit, upperLimit, lowerLimitType = 'CLOSED', upperLimitType = 'CLOSED', adminData = None):
        ws=self.rootWS()
        assert(ws is not None)
        return self._checkAndCreateDataConstraint(ws, name, 'physicalConstraint', lowerLimit, upperLimit, lowerLimitType, upperLimitType, adminData)


    def createSwBaseType(self, name, size=None, encoding=None, nativeDeclaration=None, category='FIXED_LENGTH', adminData=None):
        """
        Creates a SwBaseType object
        """
        ws=self.rootWS()
        assert(ws is not None)

        if isinstance(adminData, dict):
            adminDataObj=ws.createAdminData(adminData)
        else:
            adminDataObj = adminData
        if (adminDataObj is not None) and not isinstance(adminDataObj, autosar.base.AdminData):
            raise ValueError("adminData must be of type dict or AdminData")
        baseType = autosar.datatype.SwBaseType(name, size, encoding, nativeDeclaration, category, self, adminData)
        self.append(baseType)
        return baseType

    def createBaseType(self, name, size, encoding=None, nativeDeclaration=None, adminData=None):
        """
        AUTOSAR4

        alias for createSwBaseType
        """
        return self.createSwBaseType(name, size, encoding, nativeDeclaration, adminData)


    def createApplicationPrimitiveDataType(self, name, dataConstraint = '', compuMethod = None, unit = None, swCalibrationAccess = None, category = None, adminData = None):
        """
        AUTOSAR4

        Creates a new ApplicationPrimitiveDataType in current package.

        name: <SHORT-NAME> (str)
        dataConstraint: Name or reference to autosar.datatype.DataConstraint object (or None). Default is to automatically create a constraint based on given compuMethod
        compuMethod: Name or reference to autosar.datatype.CompuMethod object (or None).
        swCalibrationAccess: Sets the SW-CALIBRATION-ACCESS property (str['NOT-ACCESSIBLE', 'READ-ONLY', 'READ-WRITE']).
        category: The category of this element (str)
        """
        ws=self.rootWS()
        assert(ws is not None)

        adminDataObj = self._checkAdminData(adminData)
        unitObj = self._checkAndCreateUnit(ws, unit)
        dataConstraintObj, compuMethodObj = None, None
        if compuMethod is not None:
            compuMethodObj = self._findElementByRole(ws, compuMethod, 'CompuMethod')
            if compuMethodObj is None:
                raise autosar.base.InvalidCompuMethodRef(compuMethod)
        if dataConstraint is not None:
            if len(dataConstraint)==0:
                dataConstraintName = self._createDataConstraintName(ws, name)
                if compuMethodObj is not None:
                    dataConstraintObj = self._checkAndCreateDataConstraintFromCompuMethod(ws, dataConstraintName, compuMethodObj)
            else:
                dataConstraintObj = self._findElementByRole(ws, dataConstraint, 'DataConstraint')
                if dataConstraintObj is None:
                    raise autosar.base.InvalidDataConstraintRef(dataConstraint)

        unitRef = None if unitObj is None else unitObj.ref
        compuMethodRef = None if compuMethodObj is None else compuMethodObj.ref
        dataConstraintRef = None if dataConstraintObj is None else dataConstraintObj.ref

        if swCalibrationAccess is not None or dataConstraintRef is not None or compuMethodRef is not None or unitRef is not None:
            variantProps = autosar.base.SwDataDefPropsConditional(
                swCalibrationAccess = swCalibrationAccess,
                dataConstraintRef = dataConstraintRef,
                compuMethodRef = compuMethodRef,
                unitRef = unitRef
                )
        else:
            variantProps = None
        dataType = autosar.datatype.ApplicationPrimitiveDataType(name, variantProps, category, parent = self, adminData = adminDataObj)
        self.append(dataType)
        return dataType

    def createApplicationArrayDataType(self, name, element, swCalibrationAccess = None, category = 'ARRAY', adminData = None):
        """
        AUTOSAR4

        Creates a new createApplicationArrayDataType in current package.

        name: <SHORT-NAME> (str)
        swCalibrationAccess: Sets the SW-CALIBRATION-ACCESS property (str['NOT-ACCESSIBLE', 'READ-ONLY', 'READ-WRITE']).
        element: <ELEMENT> (autosar.datatype.ApplicationArrayElement)
        category: The category of this element (str). Default='ARRAY'
        """
        ws=self.rootWS()
        assert(ws is not None)

        adminDataObj = self._checkAdminData(adminData)
        if swCalibrationAccess is not None:
            variantProps = autosar.base.SwDataDefPropsConditional(
                swCalibrationAccess = swCalibrationAccess
                )
        else:
            variantProps = None
        dataType = autosar.datatype.ApplicationArrayDataType(name, element, variantProps, category, parent = self, adminData = adminDataObj)
        self.append(dataType)
        return dataType

    def createApplicationRecordDataType(self, name, elements = None, swCalibrationAccess = None, category = 'STRUCTURE', adminData = None):
        """
        (AUTOSAR4)
        Creates a new ImplementationDataType containing sub elements
        """
        ws=self.rootWS()
        assert(ws is not None)
        adminDataObj = self._checkAdminData(adminData)
        if swCalibrationAccess is not None and len(swCalibrationAccess)==0:
            swCalibrationAccess = ws.profile.swCalibrationAccessDefault

        if swCalibrationAccess is not None:
            variantProps = autosar.base.SwDataDefPropsConditional(swCalibrationAccess = swCalibrationAccess)
        else:
            variantProps = None

        dataType = autosar.datatype.ApplicationRecordDataType(name, variantProps = variantProps, category = category, adminData = adminDataObj)
        if elements is not None:
            for element in elements:
                if not isinstance(element, tuple):
                    raise ValueError('element must be a tuple')
                (elemName, elemTypeRef) = element
                elemType = ws.find(elemTypeRef, role='DataType')
                if elemType is None:
                    raise autosar.base.InvalidDataTypeRef(elemTypeRef)
                dataType.createElement(elemName, elemType.ref)
        self.append(dataType)
        return dataType

    def createImplementationDataTypeRef(self, name, implementationTypeRef, lowerLimit = None, upperLimit = None, valueTable = None, bitmask = None, offset = None, scaling = None, unit = None, forceFloat = False, dataConstraint = '', swCalibrationAccess = '', typeEmitter = None, lowerLimitType = None, upperLimitType = None, category = 'TYPE_REFERENCE', adminData = None):
        """
        AUTOSAR4

        Creates an implementation data type that is a reference (typedef) of another implementation data type
        name: name of the new data type
        typeRef: reference to implementation data type
        """
        return self._createImplementationDataTypeInternal(
            name,
            None,
            implementationTypeRef,
            lowerLimit,
            upperLimit,
            valueTable,
            bitmask,
            offset,
            scaling,
            unit,
            forceFloat,
            dataConstraint,
            swCalibrationAccess,
            typeEmitter,
            lowerLimitType,
            upperLimitType,
            category,
            adminData
        )

    def createImplementationDataTypePtr(self, name, baseTypeRef, swImplPolicy=None, category = 'DATA_REFERENCE', targetCategory = 'VALUE', adminData = None):
        """
        Creates an implementation type that is a C-type pointer to another type
        """
        ws=self.rootWS()
        assert(ws is not None)

        adminDataObj = self._checkAdminData(adminData)

        targetProps = autosar.base.SwPointerTargetProps(targetCategory, autosar.base.SwDataDefPropsConditional(baseTypeRef = baseTypeRef, swImplPolicy=swImplPolicy))
        variantProps =  autosar.base.SwDataDefPropsConditional(swPointerTargetProps = targetProps)
        implementationDataType = autosar.datatype.ImplementationDataType(name, variantProps, category = category, parent = self, adminData = adminDataObj)
        self.append(implementationDataType)
        return implementationDataType

    def createImplementationArrayDataType(self, name, implementationTypeRef, arraySize, elementName = None, swCalibrationAccess = '', typeEmitter = None, category = 'ARRAY', targetCategory = 'TYPE_REFERENCE', adminData = None):
        """
        (AUTOSAR4)
        Creates a new ImplementationDataType that references another type as an array
        """
        ws=self.rootWS()
        assert(ws is not None)
        if elementName is None:
            elementName = name
        if  swCalibrationAccess is not None and len(swCalibrationAccess)==0:
            swCalibrationAccess = ws.profile.swCalibrationAccessDefault

        if implementationTypeRef.startswith('/'):
            dataType = ws.find(implementationTypeRef)
        else:
            dataType = ws.find(implementationTypeRef, role='DataType')
        if dataType is None:
            raise autosar.base.InvalidDataTypeRef(implementationTypeRef)

        newType = autosar.datatype.ImplementationDataType(name, category = category, adminData = adminData)
        outerProps = autosar.base.SwDataDefPropsConditional(swCalibrationAccess = swCalibrationAccess)
        newType.variantProps = [outerProps]
        innerProps = autosar.base.SwDataDefPropsConditional(implementationTypeRef = implementationTypeRef)
        subElement = autosar.datatype.ImplementationDataTypeElement(elementName, targetCategory, arraySize, variantProps = innerProps)
        newType.subElements.append(subElement)
        self.append(newType)
        return newType

    def createImplementationDataType(self, name, baseTypeRef, lowerLimit = None, upperLimit = None, valueTable = None, bitmask = None, offset = None, scaling = None, unit = None, forceFloat = False, dataConstraint='', swCalibrationAccess = '', typeEmitter = None, lowerLimitType = None, upperLimitType = None, category='VALUE', adminData = None):
        return self._createImplementationDataTypeInternal(
            name,
            baseTypeRef,
            None,
            lowerLimit,
            upperLimit,
            valueTable,
            bitmask,
            offset,
            scaling,
            unit,
            forceFloat,
            dataConstraint,
            swCalibrationAccess,
            typeEmitter,
            lowerLimitType,
            upperLimitType,
            category,
            adminData
        )
    def _createImplementationDataTypeInternal(self, name, baseTypeRef, implementationTypeRef, lowerLimit, upperLimit, valueTable, bitmask, offset, factor, unit, forceFloat, dataConstraint, swCalibrationAccess, typeEmitter, lowerLimitType, upperLimitType, category, adminData):
        """
        Creates an implementation data type that wraps a base type. Defaults to value type category
        """
        ws=self.rootWS()
        assert(ws is not None)

        lowerLimit = self._convertNumber(lowerLimit)
        upperLimit = self._convertNumber(upperLimit)

        if swCalibrationAccess is not None and len(swCalibrationAccess)==0:
            swCalibrationAccess = ws.profile.swCalibrationAccessDefault

        adminDataObj = self._checkAdminData(adminData)
        unitObj = self._checkAndCreateUnit(ws, unit)

        if implementationTypeRef is not None:
            referencedType = ws.find(implementationTypeRef)
            if referencedType is None:
                raise autosar.base.InvalidDataTypeRef(implementationTypeRef)
        else:
            referencedType = None

        compuMethodObj = self._checkAndCreateCompuMethod(ws, self._createCompuMethodName(ws, name), unitObj, lowerLimit, upperLimit, offset, factor, bitmask, valueTable, referencedType, forceFloat)
        if dataConstraint is None:
            dataConstraintObj = None
        else:
            if not isinstance(dataConstraint, str):
                raise ValueError('dataConstraint argument must be None or str')
            if len(dataConstraint)==0:
            #Automatically create a data constraint on empty string
                if compuMethodObj is not None:
                    dataConstraintObj = self._checkAndCreateDataConstraintFromCompuMethod(ws, self._createDataConstraintName(ws, name), compuMethodObj)
                else:
                    if lowerLimit is None and upperLimit is None:
                        dataConstraintObj = None
                    else:
                        if upperLimit is None:
                            raise ValueError('lowerLimit cannot be None')
                        if upperLimit is None:
                            raise ValueError('upperLimit cannot be None')
                        if lowerLimitType is None:
                            lowerLimitType = 'CLOSED'
                        if upperLimitType is None:
                            upperLimitType = 'CLOSED'
                        dataConstraintObj = self._checkAndCreateDataConstraint(ws, self._createDataConstraintName(ws, name), 'internalConstraint', lowerLimit, upperLimit, lowerLimitType, upperLimitType)
            else:
                if dataConstraint.startswith('/'):
                    dataConstraintObj = ws.find(dataConstraint)
                else:
                    dataConstraintObj = ws.find(dataConstraint, role = 'DataConstraint')
                if dataConstraintObj is None:
                    raise autosar.base.InvalidDataConstraintRef(dataConstraint)

        unitRef = None if unitObj is None else unitObj.ref
        compuMethodRef = None if compuMethodObj is None else compuMethodObj.ref
        dataConstraintRef = None if dataConstraintObj is None else dataConstraintObj.ref

        variantProps = autosar.base.SwDataDefPropsConditional(swCalibrationAccess = swCalibrationAccess,
                                                              compuMethodRef = compuMethodRef,
                                                              dataConstraintRef = dataConstraintRef,
                                                              baseTypeRef = baseTypeRef,
                                                              implementationTypeRef = implementationTypeRef,
                                                              unitRef = unitRef)
        implementationDataType = autosar.datatype.ImplementationDataType(name, variantProps, typeEmitter=typeEmitter, category = category, parent = self, adminData = adminDataObj)
        self.append(implementationDataType)
        return implementationDataType

    def createImplementationRecordDataType(self, name, elements, swCalibrationAccess = '', category = 'STRUCTURE', adminData = None):
        """
        (AUTOSAR4)
        Creates a new ImplementationDataType containing sub elements
        """
        ws=self.rootWS()
        assert(ws is not None)

        if swCalibrationAccess is not None and len(swCalibrationAccess)==0:
            swCalibrationAccess = ws.profile.swCalibrationAccessDefault

        if swCalibrationAccess is not None:
            variantProps = autosar.base.SwDataDefPropsConditional(swCalibrationAccess = swCalibrationAccess)
        else:
            variantProps = None
        dataType = autosar.datatype.ImplementationDataType(name, variantProps, category = category, adminData = adminData)
        for element in elements:
            if not isinstance(element, tuple):
                raise ValueError('element must be a tuple')
            (elementName, elemTypeRef) = element
            elemType = ws.find(elemTypeRef, role='DataType')
            if elemType is None:
                raise autosar.base.InvalidDataTypeRef(elemTypeRef)
            if isinstance(elemType, autosar.datatype.ImplementationDataType):
                elementProps = autosar.base.SwDataDefPropsConditional(implementationTypeRef = elemType.ref)
            elif isinstance(elemType, autosar.datatype.SwBaseType):
                elementProps = autosar.base.SwDataDefPropsConditional(baseTypeRef = elemType.ref)
            else:
                raise NotImplementedError(type(elemType))
            implementationDataTypeElement = autosar.datatype.ImplementationDataTypeElement(elementName, 'TYPE_REFERENCE', variantProps = elementProps)
            dataType.subElements.append(implementationDataTypeElement)
        self.append(dataType)
        return dataType


    def createUnit(self, shortName, displayName = None, offset = None, scaling = None,):
        ws = self.rootWS()
        assert(ws is not None)
        if ws.roles['Unit'] is None:
            unitPackage = self
        else:
            unitPackage = ws.find(ws.roles['Unit'])
        unitElem = self._checkAndCreateUnit(ws, shortName, displayName, scaling, offset, unitPackage)
        return unitElem

    def createCompuMethodLinear(self, name, offset, scaling, lowerLimit = None, upperLimit = None, lowerLimitType = 'CLOSED', upperLimitType = 'CLOSED', unit = None, defaultValue = None, label = 'SCALING', forceFloat = True, category = 'LINEAR', adminData = None):
        """
        Alias for createCompuMethodRational
        """
        return self.createCompuMethodRational(name, offset, scaling, lowerLimit, upperLimit, lowerLimitType, upperLimitType, unit, defaultValue, label, forceFloat, category, adminData)

    def createCompuMethodRationalPhys(self, name, offset, scaling, lowerLimit = None, upperLimit = None, lowerLimitType = 'CLOSED', upperLimitType = 'CLOSED', unit = None, defaultValue = None, label = 'SCALING', forceFloat = False, useIntToPhys=False, usePhysToInt = True, category = 'LINEAR', adminData = None):
        """
        Alias for createCompuMethodRational but creates a PHYSICAL-TO-INTERNAL mapping by default
        """
        return self.createCompuMethodRational(name, offset, scaling, lowerLimit, upperLimit, lowerLimitType, upperLimitType, unit, defaultValue, label, forceFloat, useIntToPhys, usePhysToInt, category, adminData)

    def createCompuMethodRational(self, name, offset, scaling, lowerLimit = None, upperLimit = None, lowerLimitType = 'CLOSED', upperLimitType = 'CLOSED', unit = None, defaultValue = None, label = 'SCALING', forceFloat = False, useIntToPhys=True, usePhysToInt = False, category = 'LINEAR', adminData = None):
        """
        Creates a new CompuMethodRational object and appends it to the package with package-role 'CompuMethod'

        name: <SHORT-NAME> (str)
        offset: offset (int or float)
        scaling: scaling factor (int, float or rational number). This together with offset creates the numerator and denominator.
        lowerLimit: <LOWER-LIMIT> (int). Default = None.
        uppwerLimit: <UPPER-LIMIT> (int). Default = None.
        lowerLimitType: "INTERVAL-TYPE" of lowerLimit, str['OPEN', 'CLOSED']. Default='CLOSED'. Only applies when lowerLimit is not None.
        upperLimitType: "INTERVAL-TYPE" of upperLimit, str['OPEN', 'CLOSED']. Default='CLOSED'. Only applies when lowerLimit is not None.
        unit: Name of the unit (str). Default = None
        defaultValue: default value for the computation (None, int, float, str). Default = None.
        label: Label for the internally created <COMPU-SCALE> (str). Default = 'SCALING'.
        forceFloat: Forces numerator to become a floating point number (bool). Default = False.
        intToPhys: Enable internal-to-physical computation (bool). Default = True.
        physToInt: Enable physical-to-internal computation (bool). Default = False.
        category: <CATEGORY> of the CompuMethodRational (str). Default = 'LINEAR'.
        adminData: <ADMIN-DATA> for <COMPU-METHOD> (dict). Default = None.

        """
        ws = self.rootWS()
        assert(ws is not None)

        if ws.roles['CompuMethod'] is None:
            compuMethodPackage = self
        else:
            compuMethodPackage = ws.find(ws.roles['CompuMethod'])

        unitRef = None
        unitObj = self._checkAndCreateUnit(ws, unit)
        if unitObj is not None:
            unitRef = unitObj.ref

        adminDataObj = self._checkAdminData(adminData)

        compuMethod = autosar.datatype.CompuMethod(name, useIntToPhys, usePhysToInt, unitRef, category, compuMethodPackage, adminDataObj)
        (numerator, denominator) = self._calcNumeratorDenominator(scaling, forceFloat)
        if useIntToPhys:
            compuMethod.intToPhys.createRationalScaling(
                offset,
                numerator,
                denominator,
                lowerLimit,
                upperLimit,
                lowerLimitType,
                upperLimitType,
                label = label
                )
            if defaultValue is not None:
                compuMethod.intToPhys.defaultValue = defaultValue
        elif usePhysToInt:
            compuMethod.physToInt.createRationalScaling(
                offset,
                numerator,
                denominator,
                lowerLimit,
                upperLimit,
                lowerLimitType,
                upperLimitType,
                label = label
                )
            if defaultValue is not None:
                if useIntToPhys:
                    compuMethod.intToPhys.defaultValue = defaultValue
                elif usePhysToInt:
                    compuMethod.physToInt.defaultValue = defaultValue

        compuMethodPackage.append(compuMethod)
        return compuMethod

    def createCompuMethodConst(self, name, valueTable, unit = None, defaultValue = None, category = 'TEXTTABLE', adminData = None):
        useIntToPhys, usePhysToInt = True, False

        ws = self.rootWS()
        assert(ws is not None)

        if ws.roles['CompuMethod'] is None:
            compuMethodPackage = self
        else:
            compuMethodPackage = ws.find(ws.roles['CompuMethod'])
        unitRef = None
        unitObj = self._checkAndCreateUnit(ws, unit)
        if unitObj is not None:
            unitRef = unitObj.ref
        if isinstance(adminData, dict):
            adminData = autosar.base.createAdminData(adminData)

        compuMethod = autosar.datatype.CompuMethod(name, useIntToPhys, usePhysToInt, unitRef, category, compuMethodPackage, adminData)
        compuMethod.intToPhys.createValueTable(valueTable)
        if defaultValue is not None:
            compuMethod.intToPhys.defaultValue = defaultValue

        compuMethodPackage.append(compuMethod)
        return compuMethod

    def createDataTypeMappingSet(self, name, adminData = None):
        dataTypeMappingSet = autosar.datatype.DataTypeMappingSet(name, adminData = adminData)
        self.append(dataTypeMappingSet)
        return dataTypeMappingSet

    def _calcNumeratorDenominator(self, scalingFactor, forceFloat = False):

        if forceFloat:
           (numerator, denominator) = (float(scalingFactor), 1)
        else:
            f=Fraction.from_float(scalingFactor)
            if f.denominator > 10000: #use the float version in case its not a rational number
                (numerator, denominator) = (float(scalingFactor), 1)
            else:
                (numerator, denominator) = (f.numerator, f.denominator)
        return (numerator, denominator)

    def _checkAndCreateCompuMethod(self, ws, name, unitObj, lowerLimit, upperLimit, offset, scaling, bitmaskTable, valueTable, referencedType, forceFloatScaling, useCategory = True, autoLabel = True):
        """
        Returns CompuMethod object from the package with role 'CompuMethod'.
        If no CompuMethod exists with that name it will be created and then returned.
        """
        if name is None:
            return None

        category = None
        computation = autosar.datatype.Computation()
        if bitmaskTable is not None:
            category = 'BITFIELD_TEXTTABLE'
            computation.createBitMask(bitmaskTable)
        elif valueTable is not None:
            category = 'TEXTTABLE'
            computation.createValueTable(valueTable, autoLabel)
        elif offset is not None and scaling is not None:
            category = 'LINEAR'
            (numerator, denominator) = self._calcNumeratorDenominator(scaling, forceFloatScaling)
            if (lowerLimit is None) and (referencedType is not None) and (referencedType.dataConstraintRef is not None):
                constraint = ws.find(referencedType.dataConstraintRef)
                if constraint is None:
                    raise autosar.base.InvalidDataConstraintRef
                lowerLimit = constraint.lowerLimit
            if (upperLimit is None) and (referencedType is not None) and (referencedType.dataConstraintRef is not None):
                constraint = ws.find(referencedType.dataConstraintRef)
                if constraint is None:
                    raise autosar.base.InvalidDataConstraintRef
                upperLimit = constraint.upperLimit
            computation.createRationalScaling(offset, numerator, denominator, lowerLimit, upperLimit)
        if category is None:
            return None #Creating a compu method does not seem necessary

        compuMethodPackage = None
        if ws.roles['CompuMethod'] is not None:
            compuMethodPackage=ws.find(ws.roles['CompuMethod'])
        if compuMethodPackage is None:
            raise RuntimeError("No package found with role='CompuMethod'")
        compuMethodObj = compuMethodPackage.find(name)
        if compuMethodObj is not None: #Element already exists with that name?
            return compuMethodObj
        unitRef = None if unitObj is None else unitObj.ref

        useIntToPhys, usePhysToInt = True, False
        if not useCategory:
            category = None
        compuMethodObj = autosar.datatype.CompuMethod(name, useIntToPhys, usePhysToInt, unitRef, category, compuMethodPackage)
        compuMethodObj.intToPhys = computation
        compuMethodPackage.append(compuMethodObj)
        return compuMethodObj

    def _checkAndCreateDataConstraintFromCompuMethod(self, ws, name, compuMethodObj):
        constraintType = 'internalConstraint'
        if compuMethodObj.category == 'BITFIELD_TEXTTABLE':
            lowerLimit = 0
            tmp = compuMethodObj.intToPhys.upperLimit
            upperLimit = 2**int.bit_length(tmp)-1
        else:
            lowerLimit = compuMethodObj.intToPhys.lowerLimit
            upperLimit = compuMethodObj.intToPhys.upperLimit
        if (lowerLimit is not None) and upperLimit is not None:
            return self._checkAndCreateDataConstraint(ws, name, constraintType, lowerLimit, upperLimit)
        else:
            return None

    def _checkAndCreateDataConstraint(self, ws, name, constraintType, lowerLimit, upperLimit, lowerLimitType = 'CLOSED', upperLimitType = 'CLOSED', adminData = None):
        if name is None:
            return None
        dataConstraintPackage = None
        if ws.roles['DataConstraint'] is not None:
            dataConstraintPackage = ws.find(ws.roles['DataConstraint'])
        if dataConstraintPackage is None:
            raise RuntimeError("No package found with role='DataConstraint'")
        dataConstraintObj = dataConstraintPackage.find(name)
        if dataConstraintObj is not None: #Element already exists with that name?
            return dataConstraintObj
        else:
            return self._createDataConstraintInPackage(dataConstraintPackage, name, constraintType, lowerLimit, upperLimit, lowerLimitType, upperLimitType, adminData)

    def _findElementByRole(self, ws, name, role):
        if name.startswith('/'):
            return ws.find(name)
        else:
            return ws.find(name, role = role)

    def _createDataConstraintInPackage(self, dataConstraintPackage, name, constraintType, lowerLimit, upperLimit, lowerLimitType, upperLimitType, adminData):
        """
        Returns DataConstraint object from the package with role 'DataConstraint'.
        If no DataConstraint exists with that name it will be created and then returned.
        """
        rules=[]

        rules.append({'type': constraintType, 'lowerLimit':lowerLimit, 'upperLimit':upperLimit, 'lowerLimitType':lowerLimitType, 'upperLimitType':upperLimitType})
        constraint = autosar.datatype.DataConstraint(name, rules, parent=self)
        dataConstraintPackage.append(constraint)
        return constraint

    def _checkAdminData(self, adminData):
        if isinstance(adminData, dict):
            adminDataObj=autosar.base.createAdminData(adminData)
        else:
            adminDataObj = adminData
        if (adminDataObj is not None) and not isinstance(adminDataObj, autosar.base.AdminData):
            raise ValueError("adminData must be of type dict or AdminData")
        return adminDataObj

    def _createCompuMethodAndUnitV3(self, ws, name, lowerLimit, upperLimit, valueTable, bitmask, offset, scaling, unit, forceFloatScaling):
        """
        AUTOSAR3:

        If both compuMethod and unit: Returns (compuMethodRef, unitRef)
        Else if compuMethod only: Returns (compuMethodRef, None)
        Else: Returns (None, None)
        """
        semanticsPackage = None
        unitPackage = None
        compuMethodElem = None
        unitElem = None
        assert(ws is not None)
        if ws.roles['CompuMethod'] is not None:
            semanticsPackage=ws.find(ws.roles['CompuMethod'])
            if semanticsPackage is None:
                raise RuntimeError("no package found with role='CompuMethod'")
        if ws.roles['Unit'] is not None:
            unitPackage=ws.find(ws.roles['Unit'])
            if unitPackage is None:
                raise RuntimeError("no package found with role='Unit'")

        if (lowerLimit is None) and (upperLimit is None) and (valueTable is None) and (bitmask is None) and (offset is None) and (scaling is None) and (unit is None):
            return None

        if unit is not None:
            unitElem = self._checkAndCreateUnit(ws, unit, unitPackage = unitPackage)
        compuMethodElem = self._checkAndCreateCompuMethod(ws, self._createCompuMethodName(ws, name), unitElem, lowerLimit, upperLimit, offset, scaling, None, valueTable, None, forceFloatScaling, useCategory = False, autoLabel = False)
        if (compuMethodElem is not None) and (unitElem is not None):
            compuMethodElem.unitRef = unitElem.ref
        return None if compuMethodElem is None else compuMethodElem.ref

    def _checkAndCreateUnit(self, ws, shortName, displayName = None, factor = None, offset = None, unitPackage = None):
        if shortName is None:
            return None
        if unitPackage is None:
            unitPackage = ws.find(ws.roles['Unit'])
            if unitPackage is None:
                raise RuntimeError("no package found with role='Unit'")
        assert(isinstance(unitPackage, autosar.package.Package))
        unitElem = unitPackage.find(shortName)
        if unitElem is None:
            unitElem = self._createUnitInPackage(unitPackage, shortName, displayName, factor, offset)
        return unitElem

    def _createUnitInPackage(self, unitPackage, shortName, displayName, factor, offset):
        if displayName is None:
            displayName = shortName
        unitElem = autosar.datatype.Unit(shortName, displayName, factor, offset)
        unitPackage.append(unitElem)
        return unitElem

    def _convertNumber(self, number):
        """
        Attempts to convert argument to int.
        If that fails it tries to convert to float.
        If that fails it returns the argument as a string.
        """
        retval = None
        if number is not None:
            try:
                retval = int(number)
            except ValueError:
                try:
                    retval = float(number)
                except ValueError:
                    retval = str(number)
        return retval

    def _createCompuMethodName(self, ws, name):
        return name + ws.profile.compuMethodSuffix

    def _createDataConstraintName(self, ws, name):
        return name + ws.profile.dataConstraintSuffix
