# This file is part of the Coral project.
# Please read the copyright notice in the LICENSE file.

#
# UML14_to_SMD01 - In-memory UML 1.4 Model to SMD 0.1 Model Transformer
#
# SYNOPSIS
#
# UML14_to_SMD01 inputfile [ outputfile ]
#
# DESCRIPTION
#
# Simple script to convert a UML 1.4 Model to SMD 0.1 model.
# This is used because we don't have a SMD editor, while we do have a
# UML editor
#
#
# AUTHORS
#
# Tomas Lillqvist 2005
#
#
#

# This script converts UML14 into SMD01
# Model -> Language
# Class -> ElementDefinition
# Association -> Property
# Generalizations -> sub-, super-classes.

# It works by right clicking on a UML14 Model and selecting convert in
# the UML2SMD contextmenu entry.  If there is an element in the model
# with the same name as an element in CORE01 it will use the CORE
# element in the language instead of creating a new ElementDefinition.
# Otherwise it wouldn't be possible to use the CORE types.

# The convert function takes as input a UML14.Model and returns a
# SMD01.Language. Very simple.

import coral

UML=coral.lang.UML14
SMD=coral.lang.SMD01

element2ed={} # contains the mapping from UML to SMD

def getTaggedValue(self,name,defaultValue=None):
    for tv in self.taggedValue:
        if str(tv.name)==name:
            return str(tv.dataValue)
    return defaultValue
    
def convert(model):
    global element2ed
    element2ed={}
    lang=SMD.Language(name=model.name)
    lang.__setXMIid__(model.__XMIid__())
    lang.version=int(getTaggedValue(model,"version","1"))
    lang.revision=int(getTaggedValue(model,"revision","0"))
    lang.URInamespace=getTaggedValue(model,"URInamespace","http://mde.abo.fi/"+str(model.name)+"/"+str(lang.version)+"."+str(lang.revision))
    element2ed[model]=lang
    transform(model,lang)
    return lang

def fixProperty(P, p1, p2):
    # Configures property P to reflect the attributes of association ends p1 and p2.
    # P gets:
    #  name of p2 or, if not set, p2.participant's lowercase name
    #  aggregation from p1
    #  ordered from p2
    #  multiplicity from p2

    global element2ed
    P.name = p2.name
    P.__setXMIid__(p2.__XMIid__())
    P.element_kind = element2ed[p2.participant]

    if not element2ed.get(p1.participant,None):
        print p1
        print p1.participant

    element2ed[p1.participant].properties.append(P)

    if not p2.name:
        P.name = str(p2.participant.name).lower()

    if p2.multiplicity.range:
        m = p2.multiplicity.range[0]
        P.multiplicity_upper = m.upper
        P.multiplicity_lower = m.lower

    if P.multiplicity_upper == 0:
        P.multiplicity_upper = 1

    if str(p2.ordering) == "ordered":
        P.isOrdered = True

    if str(p1.aggregation) == "composite":
        P.type = "composition"
        
        
def transform(model,lang):
    global element2ed
    lang.isAbstract=model.isAbstract
    for elem in model.ownedElement:
        if elem.oclIsKindOf(UML.Class):
            pos=unicode(elem.name).find("::") # For example SMD01::, CORE01::
            if pos>0:
                if not element2ed.get(elem,None):
                    META=getattr(coral.meta, elem.name[:pos])
                    ed=META.elements[str(elem.name[pos+2:])]
                    element2ed[elem]=ed
                    continue
                    
            if elem.stereotype.exists(lambda s: s.name == "enum" or s.name == "enumeration"):
                ed=SMD.EnumerationDefinition(name=elem.name)
            else:
                ed=SMD.ElementDefinition(name=elem.name)


            ed.__setXMIid__(elem.__XMIid__())
            lang.elements.append(ed)
            element2ed[elem]=ed
            ed.isAbstract=elem.isAbstract
                
            if elem.stereotype.exists(lambda s: str(s.name)=="root"):
                lang.root_ed=ed

            if not elem.generalization and ed.oclIsKindOf(SMD.ElementDefinition):
                #print "No superclass specified for ",elem
                #print "Setting CORE01::AnyAbstractElement as superclass"
                ed.superclasses.append(coral.meta.CORE01.AnyAbstractElement)
                
    for elem in model.ownedElement: # Recursively create contained Languages from Packages.
        if elem.oclIsKindOf(UML.Package):
            ed=SMD.Package(package=lang, name=elem.name)
            ed.__setXMIid__(elem.__XMIid__())
            element2ed[elem]=ed
            transform(elem, ed)

    for elem in model.ownedElement: # After creaing all ElementDefinitions the properties are added.
        if elem.oclIsKindOf(UML.Generalization):
            element2ed[elem.child].superclasses.append(element2ed[elem.parent])

        if elem.oclIsKindOf(UML.Association):
            end1 = elem.connection[0]
            end2 = elem.connection[1]

            # Create properties for the element definitions

            end1Prop=None
            end2Prop=None
            
            if end2.isNavigable == "true":
                # Properties can only be added to ElementDefinition, not to DatatypeDefinitions.
                if not element2ed[end1.participant].oclIsKindOf(SMD.DatatypeDefinition):
                    end1Prop = SMD.Property()
                    fixProperty(end1Prop, end1, end2)

            if end1.isNavigable == "true":
                # Properties can only be added to ElementDefinition, not to DatatypeDefinitions.
                if not element2ed[end2.participant].oclIsKindOf(SMD.DatatypeDefinition):
                    end2Prop = SMD.Property()
                    fixProperty(end2Prop, end2, end1)

            # Set opposite:Property
            if end1Prop and end2Prop:
                end1Prop.opposite = end2Prop
                end2Prop.opposite = end1Prop

            # Set type of property to composite if needed
            if end2Prop and str(end1.aggregation) == "composite":
                end2Prop.multiplicity_lower=0
                end2Prop.multiplicity_upper=1
                
            if end1Prop and str(end2.aggregation) == "composite":
                end1Prop.multiplicity_lower=0
                end1Prop.multiplicity_upper=1


            #Set multiplicity for EnumerationDefinition properties to 1..1
            if end1Prop and element2ed[end2.participant].oclIsKindOf(SMD.EnumerationDefinition):
                end1Prop.multiplicity_lower=1
                end1Prop.multiplicity_upper=1

            if end2Prop and element2ed[end1.participant].oclIsKindOf(SMD.EnumerationDefinition):
                end2Prop.multiplicity_lower=1
                end2Prop.multiplicity_upper=1
                

        if elem.oclIsKindOf(UML.Class):
            # Attributes become composite properties with upper multiplicity 1
            for attr in elem.feature.select(lambda f: f.oclIsKindOf(UML.Attribute)):
                if elem.stereotype.exists(lambda s: s.name == "enum" or s.name == "enumeration"):
                    element2ed[elem].values.append(unicode(attr.name))
                    continue

                attr_ed=element2ed.get(attr.type, None)
                if not attr_ed:
                    pos=unicode(elem.name).find("::") # For example SMD01::, CORE01::
                    if pos>0:
                        META=getattr(coral.meta, elem.name[:pos])
                        ed=META.elements[str(elem.name[pos+2:])]
                        element2ed[elem]=ed
                    
                    else:
                        # BUG does not set XMIid?
                        # This should not happen.
                        assert 0
                        attr_ed=SMD.ElementDefinition(name=attr.type.name)
                        element2ed[attr.type]=attr_ed

                newprop=SMD.Property(name=attr.name)
                newprop.__setXMIid__(attr.__XMIid__())
                newprop.element_kind=attr_ed

                if attr.ordering=="ordered":
                    newprop.isOrdered = True
                    
                m = attr.multiplicity.range
                if m:
                    newprop.multiplicity_lower = m[0].lower
                    newprop.multiplicity_upper = m[0].upper
                else:
                    newprop.multiplicity_lower = 1
                    newprop.multiplicity_upper = 1

                newprop.type = "composition"
                element2ed[elem].properties.append(newprop)

                if attr.initialValue and attr.initialValue.body:
                    newprop.defaultValue = attr.initialValue.body
                    
            # Operations have to be written in Python
            # All code goes into the specification slot of UML14 Operation.
            # UML14 Parameters are not used.
            for oper in elem.feature.select(lambda f: f.oclIsKindOf(UML.Operation)):
                newOper=SMD.Operation(name=oper.name)
                for line in unicode(oper.specification).split('\n'):
                    newOper.code.append(line)

                newOper.language="python"
                newOper.__setXMIid__(oper.__XMIid__())
                element2ed[elem].operations.append(newOper)
                
        
def main():
    import sys
    f = sys.argv[1]

    L = coral.lang.UML14
    p = coral.loadDocument(f)
    m_uml = None
    for i in p.contents():
        if i.oclIsKindOf(L.Model):
            m_uml = i
            break
    assert m_uml

    m_smd = convert(m_uml)
    if len(sys.argv) > 2:
        outfile = sys.argv[2]
    else:
        outfile = "/dev/stdout"

    prj = coral.Project()
    prj.setFilename(outfile)
    m_smd.__setProject__(prj)
    prj.setXMIFormat(p.getXMIFormat())
    coral.saveDocument(prj)

if __name__=='__main__':
    main()
