#!/usr/bin/env python
# -*- coding: utf-8 -*-

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

# uml2xmidigen - Generates diagram element generators based on a UML model
#
# SYNOPSIS
#
# uml2xmidigen [-o outputfile] models
#
#
# DESCRIPTION
#
# models: One or more UML 1.4 models describing the XMI-DI hierarchy of model elements
# 
# outputfile: The file where the output code is written. If none specified, the output
# is written to stdout.
# 
# uml2xmidigen reads the models describing how the XMI-DI hierarchy should be generated
# and generates diagram element generator classes based on the definitions in the models.
# uml2xmidigen also generates synchronization code to maintain the specified XMI-DI
# hierachy, that can be run from an observer.
#
# OPTIONS
# -h, --help : help 
# -o "outputfile": Writes the output to "outputfile". If omitted, output is directed to stdout
#
# EXAMPLES
#
# uml2xmidigen ClassDiagram.xml degen.py
# 
# AUTHORS
#
# Torbjörn Lundkvist 2004
# Coral Team - 2004-08



import coral
import re, string
from coral.lgen.codeartifact import CodeArtifact
import getopt,sys

DI = coral.lang.XMIDI20

perpetual = 1
path = []
isOrdered = True

importLanguage=True

def __comparefunction(x,y):
    if x.name>y.name:
        return 1
    return -1

def __otherEnd(associationend):
    association = associationend.association
    idx = 0
    if association.connection[0] == associationend:
        idx = 1
    return association.connection[idx]

def __getParentElement(node):
    """ This method returns the parent of a certain class in the class tree. """
    try:
        idx = path.index(node)
    except:
        return path[-1]

    return path[idx-1]

def __getChildLinks(node):
    """ This method lists the links from  a class and returns them sorted."""
    seq = [i for i in node.association if i.aggregation == "composite"]
    seq.sort(lambda x,y: cmp(x.association.name, y.association.name))
    return seq

def __getChild(ass_end):
    return __otherEnd(ass_end).participant

def __getAssociation(ass_end):
    return ass_end.association

def __synchronizeGraphNode(thisNode):
    if len(path) >= 1 and thisNode not in path and str(thisNode.name) in ["GraphNode", "GraphEdge"]:
        return True
    else:
        return False

## This is just temporary until we know the right order dynamically
def __getOrder(thisNode):
    ass = __getLinkToElement(thisNode)
    if ass:    
        return str(ass.name)

def __getLinkToElement(thisNode):
    idx = path.index(thisNode)
    assert idx > 0
    prevNode = path[idx-1]
    right_assoc = None
    assend = [i for i in thisNode.association if i.aggregation != "composite"]
    for ae in assend:
        if __otherEnd(ae).participant == prevNode:
            right_assoc = __otherEnd(ae).association
            break
    return right_assoc

def __getLinkBetweenElements(deeper, parent):

    assend = [i for i in deeper.association if i.aggregation != "composite"]
    for ae in assend:
        if __otherEnd(ae).participant == parent:
            right_assoc = __otherEnd(ae).association
            break
    return right_assoc


def __getSeparator(node):
    separator = None
    for dep in node.clientDependency:
        if dep.name == "separator":
            separator = dep.supplier[0]
            break
    return separator

def __getSurroundingNodes(node):
    parent = __getParentElement(node)
    childlinks = __getChildLinks(parent)
    i=0
    for cl in childlinks:
        child = __getChild(cl)
        if child == node:
            break
        i += 1
    first = i

    if i != 0:
        for idx in xrange(i-1, -1,-1):
            for tv in __getAssociation(childlinks[idx]).taggedValue:
                m = re.search("^constraint:\s*(.*)", str(tv.dataValue))
                if not m:
                    first = idx
    last = first
    if last == len(childlinks)-1:
        last = -1
    else:
        for idx2 in xrange(last+1, len(childlinks)):
            if str(__getChild(childlinks[idx2]).name) not in ["?", "GraphNode", "GraphEdge"]:
                last=idx2
                break
            
    prevnode = __getChild(childlinks[first-1])
    nextnode = __getChild(childlinks[last])

    
    return first, prevnode, last, nextnode

def __getStartAndEnd(parentnode):

    startname=""
    endname=""
    
    association_ends = __getChildLinks(parentnode)
    for ae in association_ends:
        child= __getChild(ae)
        for tv in child.taggedValue:
            startm = re.search("start:\s*(.*)", str(tv.dataValue))
            if startm:
                startname=str(child.name)
            endm = re.search("end:\s*(.*)", str(tv.dataValue))
            if endm:
                endname=str(child.name)

    return startname, endname

def __gen_xmidi(self, selfstring, gn, ca, metaclass, containerstring):
    """ This method generates the createDiagramElement method. """
    global perpetual
    nodename = "node_" + str(perpetual)
    perpetual += 1

    # create diagram element with children
    byExt = False
    if str(gn.name) == "?":
        byExt = True
        ca.add("if "+selfstring+":")
        ca.indent(1)
        ca.add("ext = DiagramElementGenerator.getExtension("+selfstring+".__type__())")
        ca.add("if ext:")
        ca.indent(1)
        ca.add(nodename + " = ext.createDiagramElement("+selfstring+", xparent="+containerstring+", container="+containerstring+")")
        ca.indent(-2)
    elif str(gn.name) == "GraphNode":
        # Create a graphnode, use defaultsize from the model if given.
        # Position is taken from the action.
        if gn.feature and "width" in [attr.name for attr in gn.feature]:
            width=100
            height=100
            for attr in gn.feature:
                if attr.name=="width":
                    width=str(attr.initialValue.body)
                elif attr.name=="height":
                    height=str(attr.initialValue.body)
        
            ca.add(nodename + " = self.createGraphNode("+selfstring+",size=DI.Dimension(width="+str(width)+",height="+str(height)+"), **kwargs)")
        else:
            noXMI = False
            for attr in gn.feature:
                if attr.name=="noXMI":
                    noXMI=True
            if noXMI:
                ca.add(nodename+" = None")
            else:
                if gn.supplierDependency:
                    ca.add(nodename + " = self.createGraphNode("+selfstring+", **kwargs)")
                else:
                    ca.add(nodename + " = self.createGraphNode("+selfstring+")")
            
    elif str(gn.name) == "GraphEdge":
        # Create a graphEdge, with arguments taken from the action
        ca.add(nodename + " = self.createGraphEdge("+selfstring+", **kwargs)")
        
    else:
        # Create a simple GraphNode
        ca.add(nodename + " = self.createSimpleGraphNode(\""+gn.name+"\", container="+containerstring+")")

    # The associations tell us what child elements the GraphElement should have 
    association_ends = __getChildLinks(gn)

    ss = selfstring
    
    for ae in association_ends:
        association = __getAssociation(ae)
        child = __getChild(ae)
        extra_indent = 0
        separator = None
        for tv in association.taggedValue:
            # Get the information from the tagged value of the association

            # Insert constraints
            m = re.search("^constraint:\s*(.*)", str(tv.dataValue))
            if m:
                extra_indent += 1
                ca.add("if " + str(m.group(1)) + ":")
                ca.indent(1)
            # which property are we refering to
            m = re.search("property:\s*(.*)", str(tv.dataValue))
            if m:
                separator = __getSeparator(child)
                # insert separators between the elements
                if separator:
                    idx_name = "idx_" + str(perpetual)
                    perpetual += 1
                    ca.add(idx_name + " = 0")

                # get the property of the abstract element
                ca.add("attr = getattr(" + selfstring + ", \"" + str(m.group(1)) +"\")")

                # check if the property is a list or not
                if metaclass.getPropertyByName(str(m.group(1))).getMultUpper() == 1:
                    ss = "self_" + str(perpetual)
                    perpetual += 1
                    ca.add(ss+"=attr")
                                
                else:
                    # it is a list
                    ss = "self_" + str(perpetual)
                    perpetual += 1
                    ca.add("for " + ss + " in attr:")
                    extra_indent += 1
                    ca.indent(1)
                    # retrieve is a more specific constraint that tells us which elements should have a graphnode
                    retrievestring = re.search("retrieve:\s*(.*)", str(tv.dataValue))
                    if retrievestring:
                        ca.add("if "+ss+" in "+str(retrievestring.group(1))+":")
                        ca.indent(1)
                        extra_indent += 1
                        
        ## Create graphnodes recursevily      
        c = __gen_xmidi(self, ss, child, ca, metaclass, nodename)
        
        if separator:
            # do not add a separator after the last element
            if retrievestring:
                ca.add("if " + idx_name + " != len("+str(retrievestring.group(1))+")-1:")
            else:
                ca.add("if " + idx_name + " != len(attr)-1:")
            ca.indent(1)
            sep_name = __gen_xmidi(self, ss, separator, ca, metaclass, nodename)
            ca.indent(-1)
            ca.add(idx_name + " += 1")

        # reset the indentation
        ca.indent(-extra_indent)

    return nodename


def __gen_xmidi_synch_multi(self, selfstring, gn, ca, metaclass, containerstring, synchproperty):
    """ This method generates the synchronization of a compartment shared
    by two or more property synchronizations. It also synchronizes all simple
    graphnodes, i.e. adds or removes them whenever the mapped constraint
    specifies so. The method is non-recursive, and attempts to preserve all
    existing graphnodes. """
    
    global perpetual

    # The associations tell us what child elements the GraphElement should have 
    association_ends = __getChildLinks(gn)

    ss = selfstring
    ca.add("idx = 0")
    for ae in association_ends:
        association = __getAssociation(ae)
        child = __getChild(ae)
        extra_indent = 0
        separator = None
        for tv in association.taggedValue:
            # Get the information from the tagged value of the association
            nodeconstraint = ""
            nodeproperty = ""
            retrievestring = ""
            # Insert constraints
            m = re.search("^constraint:\s*(.*)", str(tv.dataValue))
            if m:
                nodeconstraint = str(m.group(1))
            # which property are we refering to
            m = re.search("property:\s*(.*)", str(tv.dataValue))
            if m:
                nodeproperty = str(m.group(1))
                separator = __getSeparator(child)
            m = re.search("retrieve:\s*(.*)", str(tv.dataValue))
            if m:
                retrievestring = str(m.group(1))

        if child.name == "?":
            if nodeconstraint:
                ca.add("if "+nodeconstraint+":")
                extra_indent += 1
                ca.indent(1)
            ca.add("attr = getattr("+selfstring+", \""+nodeproperty+"\")")
            singleslot = bool(metaclass.getPropertyByName(nodeproperty).getMultUpper() == 1)
            # check if the property is a list or not
            if singleslot:
                ss = "self_" + str(perpetual)
                perpetual += 1
                ca.add(ss+"=attr")

            else:
                # it is a list
                ss = "self_" + str(perpetual)
                perpetual += 1
                ca.add("for " + ss + " in attr:")
                extra_indent += 1
                ca.indent(1)

            if retrievestring:
                ca.add("if "+ss+" and "+ss+" in "+retrievestring+":")
            else:
                ca.add("if "+ss+":")
            ca.indent(1)

            if singleslot:
                ca.add("if len(old_di)!=0 and len(old_di)>idx and not hasattr(old_di[idx].semanticModel, \"typeInfo\") and old_di[idx].semanticModel.element == "+ss+":")
                ca.indent(1)
                ca.add("idx += 1")
                ca.indent(-1)
                if nodeproperty == synchproperty:
                    ca.add("elif len(old_di)==0 or len(old_di)>=idx or not hasattr(old_di[idx].semanticModel, \"typeInfo\") and old_di[idx].semanticModel.element != "+ss+":")
                    ca.indent(1)
                    ca.add("ext = DiagramElementGenerator.getExtension("+ss+".__type__())")
                    ca.add("if ext:")
                    ca.indent(1)
                    newnode = "node_" + str(perpetual)
                    perpetual += 1
                    ca.add(newnode+" = ext.createDiagramElement("+ss+", xparent="+containerstring+")")
                    ca.add("old_di.insert("+newnode+",idx)")
                    if str(gn.name)=="GraphEdge":
                        ca.add("if "+ containerstring +".oclIsKindOf(DI.GraphEdge) and len("+containerstring+".waypoints) >= 2:")
                        ca.indent(1)
                        ca.add(newnode+".position.x = ("+containerstring+".waypoints[0].x+"+containerstring+".waypoints[-1].x)/2")
                        ca.add(newnode+".position.y = ("+containerstring+".waypoints[0].y+"+containerstring+".waypoints[-1].y)/2-20")
                        ca.indent(-1)
                    ca.add("idx += 1")
                    ca.indent(-2)
                    if nodeconstraint:
                        ca.indent(-2)
                        extra_indent -= 1
                    else:
                        ca.indent(-1)
                    ca.add("else:")
                    ca.indent(1)
                    ca.add("if len(old_di)!=0 and len(old_di)>idx and not hasattr(old_di[idx].semanticModel, \"typeInfo\") and old_di[idx].semanticModel.element in old_am:")
                    ca.indent(1)
                    ca.add("old_di.drop_index(idx)")
                    ca.indent(-1)
            else:
                ca.add("if len(old_di)!=0 and len(old_di)>idx and not hasattr(old_di[idx].semanticModel, \"typeInfo\") and old_di[idx].semanticModel.element in attr:")
                ca.indent(1)
                ca.add("idx += 1")
                ca.indent(-1)
                if nodeproperty == synchproperty:
                    ca.add("elif len(old_di)==0 or len(old_di)>=idx or not hasattr(sm, \"typeInfo\") and sm.element not in attr:" )
                    ca.indent(1)
                    ca.add("ext = DiagramElementGenerator.getExtension("+ss+".__type__())")
                    ca.add("if ext:")
                    ca.indent(1)
                    newnode = "node_" + str(perpetual)
                    perpetual += 1
                    ca.add(newnode+" = ext.createDiagramElement("+ss+", xparent="+containerstring+")")
                    ca.add("old_di.insert("+newnode+",idx)")
                    if str(gn.name)=="GraphEdge":
                        ca.add("if "+ containerstring +".oclIsKindOf(DI.GraphEdge) and len("+containerstring+".waypoints) >= 2:")
                        ca.indent(1)
                        ca.add(newnode+".position.x = ("+containerstring+".waypoints[0].x+"+containerstring+".waypoints[-1].x)/2")
                        ca.add(newnode+".position.y = ("+containerstring+".waypoints[0].y+"+containerstring+".waypoints[-1].y)/2-20")
                        ca.indent(-1)
                    ca.add("idx += 1")
                    ca.indent(-2)

            ca.indent(-1)
            ca.indent(-extra_indent)
            ca.add("")

        elif child.name not in ["GraphNode","GraphEdge"]:
            newnode = "node_"+str(perpetual)
            perpetual += 1
            if nodeconstraint:
                ca.add("if "+nodeconstraint+":")
                ca.indent(1)
                extra_indent += 1
            ca.add("if len(old_di)!=0 and len(old_di)>idx and hasattr(old_di[idx].semanticModel, \"typeInfo\") and old_di[idx].semanticModel.typeInfo == \""+child.name+"\":")
            ca.indent(1)
            ca.add("idx += 1")
            ca.indent(-1)
            ca.add("else:")
            ca.indent(1)
            ca.add(newnode +" = self.createSimpleGraphNode(\""+child.name+"\")")
            ca.add("old_di.insert("+newnode+",idx)")
            ca.add("idx += 1")
            ca.indent(-1)

            if nodeconstraint:
                ca.indent(-1)
                ca.add("else:")
                ca.indent(1)
                ca.add("if len(old_di)!=0 and len(old_di)>idx and hasattr(old_di[idx].semanticModel, \"typeInfo\") and old_di[idx].semanticModel.typeInfo == \""+str(child.name)+"\":")
                ca.indent(1)
                ca.add(newnode+" = old_di[idx]")
                ca.add("old_di.remove("+newnode+")")
                ca.indent(-1)

            ca.indent(-extra_indent)
            ca.add("")

    ca.add("for e in xrange(idx, len(old_di)):")
    ca.indent(1)
    ca.add("old_di[e].container = None")
    ca.indent(-1)
    ca.add("")


def createIsMinorElement( gn, ca ):
    """ Every DEG class gets a isMinorElement method.
    The default value is TRUE. """
    
    value="True"
    for attr in gn.feature:
        if str(attr.name)=="isMinorElement":
           value=str(attr.initialValue.body)
        
    ca.add("return "+value)

def createSuitableDiagrams( gn, ca ):
    """ Creates the suitableDiagrams method for the DEG classes,
    whose graphElements can be put in a diagram. """
    
    for attr in gn.feature:
        if attr.name=="suitableDiagrams":
            ca.add("def suitableDiagrams( self , element=None, xparent=None):")
            ca.indent(1)
            value=str(attr.initialValue.body)
            ca.add("return " + value)
            ca.indent(-1)
            ca.add("")

def createSynchronizeContents(self, selfstring, gn, ca, metaclass, property=""):
    """ This method generates the synchronizeContents method. """
    global perpetual, isOrdered, path
    nodename = "node_" + str(perpetual)
    perpetual += 1
    firstTime = True
    synchronize = __synchronizeGraphNode(gn)
    noXMI=False
    synchronizeEnds=False
    synchronizeProperty=""
    
    for attr in gn.feature:
        if attr.name=="noXMI":
            noXMI=True
        if attr.name=="synchronizeEnds":
            synchronizeEnds=True
            if attr.initialValue:
                synchronizeProperty = str(attr.initialValue.body)
                


    ## This block generates the link-synchronisation of the "easier" graphedges
    if str(gn.name) == "GraphEdge":
        for tv in gn.taggedValue:
            mult = ""
            ind = []
            m = re.search("index1:\s*(.*)\s", str(tv.dataValue))
            n = re.search("index2:\s*(.*)\s", str(tv.dataValue))
            if m:
                m2 = re.search("\[\s*element\.(.*)\s*\,\s*element\.(.*)\]", str(m.group(1)))
                if m2:
                    mult = ""
                    ind.append((str(m2.group(1)), 0))
                    ind.append((str(m2.group(2)), 1))
                else:
                    m3 = re.search("element\.(.*)", str(m.group(1)))
                    if m3 and n:
                        n2 = re.search("element\.(.*)", str(n.group(1)))
                        if n2:
                            mult = "[0]"
                            ind.append((str(m3.group(1)), 0))
                            ind.append((str(n2.group(1)), 1))

            for pr_name, node_index in ind:
                ca.add("if property == \""+ pr_name + "\""+":")
                ca.indent(1)
                ca.add("new_di = None")
                ca.add("if new_am:")
                ca.indent(1)
                ca.add("new_di = new_am"+ mult  +".xmidi_get_graphelements()")
                ca.indent(-1)
                ca.add("if len(elementGN.anchor)>1:")
                ca.indent(1)
                ca.add("elementGN.anchor.drop_index("+str(node_index)+")")
                ca.indent(-1)
                ca.add("if new_di:")
                ca.indent(1)
                ca.add("di = geInDiagram(new_di, elementGN.__getRoot__())")
                ca.add("if di:")
                ca.indent(1)
                ca.add("gc = DI.GraphConnector(graphElement = di[0])")
                ca.add("elementGN.anchor.insert(gc, "+str(node_index)+")")
                ca.indent(-3)
                ca.add("")


    if str(gn.name) == "GraphNode" and synchronizeEnds and synchronizeProperty:
        ca.add("if property == \""+ synchronizeProperty + "\""+":")
        ca.indent(1)
        ca.add("if element.__parent__():")
        ca.indent(1)
        ca.add("prop = element.__parent__().__getSlotByName__(\"connection\").getProperty().getOppositeProperty().getName()")
        ca.add("op2con = element.__getSlotByName__(prop)[0]")
        ca.add("xmi_edges = op2con.xmidi_get_graphelements()")
        ca.add("for edge in xmi_edges:")
        ca.indent(1)
        ca.add("if elementGN.__getRoot__() == edge.__getRoot__():")
        ca.indent(1)
        ca.add("end=1")
        if noXMI:
            ca.add("if edge.semanticModel.element.connection[0]."+synchronizeProperty+" == elementGN.semanticModel.element:")
        else:
            ca.add("if edge.semanticModel.element.connection[0] == elementGN.semanticModel.element:")
        ca.indent(1)
        ca.add("end=0")
        ca.indent(-1)
        ca.add("edge.anchor[end].graphEdge.drop(edge)")
        ca.add("if new_am:")
        ca.indent(1)
        ca.add("for gnode in new_am.xmidi_get_graphelements():")
        ca.indent(1)
        ca.add("if gnode.__getRoot__() == edge.__getRoot__():")
        ca.indent(1)
        ca.add("new_gnode=gnode")
        ca.add("break")
        ca.indent(-2)
        ca.add("edge.anchor.insert(DI.GraphConnector(graphElement=new_gnode),end)")
        ca.indent(-2)
        ca.add("else:")
        ca.indent(1)
        ca.add("continue")
        ca.indent(-3)
        ca.add("")
        ca.indent(-1)

        
            
    if str(gn.name) == "?" or synchronize:

        con = ""
        ca.indent(1)
        ## Find out which containers need to be found/created to proceed with synch
        for cnt in xrange(1,len(path)):
            curnode = path[cnt]
            path_order = __getOrder(curnode)

            if firstTime:
                oldCon = "elementGN"
                firstTime = False
            else:
                oldCon = con
            con = "container_" + str(perpetual)
            perpetual += 1
            ca.add(con + ', ' + con+'_idx = searchForSimpleGraphNode(' + oldCon + '.contained, \"' + str(curnode.name) + '\")')
            ca.add("if not "+con+":")
            ca.indent(1)
            link2this = __getLinkToElement(curnode)
            createconstraint = None
            if link2this:
                for tv in link2this.taggedValue:
                    m = re.search("^constraint:\s*(.*)", str(tv.dataValue))
                    if m:
                        createconstraint = str(m.group(1))
                        break
                    
            if createconstraint:
                ca.add("if "+createconstraint+":")
                ca.indent(1)
                ca.add(con+' = self.createSimpleGraphNode(\"'+str(curnode.name)+'\")')

                parent = __getParentElement(path[-1])
                childlinks = __getChildLinks(parent)
                i=0
                for cl in childlinks:
                    child = __getChild(cl)
                    if child == path[-1]:
                        break
                    i += 1
                first = i
                node_index = i

                if i != 0:
                    for idx in xrange(i-1, -1,-1):
                        for tv in __getAssociation(childlinks[idx]).taggedValue:
                            m = re.search("^constraint:\s*(.*)", str(tv.dataValue))
                            if not m:
                                first = idx
                            else:
                                first -= 1

                if node_index == 0:
                    ca.add(oldCon+".contained.insert("+con+",0)")
                else:
                    ca.add("ins_pt="+str(first-1))                    
                    for idx2 in xrange(first-1, node_index):
                        ca.add("srch_cn, srch_idx = searchForSimpleGraphNode(" + oldCon + ".contained, \"" + str(__getChild(childlinks[idx2]).name) + "\")")
                        ca.add("if srch_idx != -1:")
                        ca.indent(1)
                        ca.add("ins_pt = srch_idx")
                        
                        
                        beforeconstraint = None
                        link2this = __getLinkBetweenElements(__getChild(childlinks[idx2]), __getParentElement(path[-1]))
                        if link2this:
                            for tv in link2this.taggedValue:
                                m = re.search("^constraint:\s*(.*)", str(tv.dataValue))
                                if m:
                                    beforeconstraint = str(m.group(1))
                                    break
                        if beforeconstraint:
                            ca.indent(-1)
                            ca.add("else:")
                            ca.indent(1)
                            ca.add("if "+beforeconstraint+":")
                            ca.indent(1)
                            ca.add('srch_cn = self.createSimpleGraphNode(\"'+str(__getChild(childlinks[idx2]).name)+'\")')
                            ca.add("ins_pt += 1")
                            ca.add(oldCon+".contained.insert(srch_cn,ins_pt)")

                            ca.indent(-1)
                        ca.indent(-1)
                
                    ca.add(oldCon+".contained.insert("+con+",ins_pt+1)")
                ca.indent(-1)
                ca.add("else:")
                ca.indent(1)
                ca.add("continue")
                ca.indent(-1)
            else:
                ca.add("continue")

            ca.indent(-1)

        if isOrdered:

            if not con:
                con = "elementGN"
                oldCon = "elementGN"
                
            first, prevnode, last, nextnode = __getSurroundingNodes(gn)

            #check for separator
            separator = __getSeparator(gn)


            ca.add("existing = {}")
            ca.add("for xmidi in " + con+ ".contained:")
            ca.indent(1)
            ca.add("existing[xmidi.semanticModel.element] = xmidi")
            ca.indent(-1)
            ca.add("old_di = " + con + ".contained")

            if first != 0:
                ca.add("start_cont , start_idx = searchForSimpleGraphNode(old_di, \""+ str(prevnode.name) +"\")")
            else:
                ca.add("start_idx = -1")

            # check position of the right bound-node
            if last != -1:
                ca.add("end_cont , end_idx = searchForSimpleGraphNode(old_di, \""+ str(nextnode.name) +"\")")
            else:
                ca.add("end_idx = len(old_di)")

            # kill old elements
            ca.add("")
            ca.add("nodes2kill = []")
            ca.add("for i in xrange(start_idx+1, end_idx):")
            ca.indent(1)
            ca.add("sm = old_di[i].semanticModel")
            ca.add("elem = sm.element")
            # This we have to do until we can kill a GN when sm.element (UML1SMB) becomes None
            ca.add("if (elem and elem not in elements2be) or (not hasattr(sm, \"typeInfo\") and sm.element is None):")
            ca.indent(1)
            ca.add("nodes2kill.append(old_di[i])")
            # remove old separators in the same run
            if separator:
                ca.indent(-1)
                ca.add("if hasattr(sm, \"typeInfo\") and sm.typeInfo == \""+str(separator.name)+"\":")
                ca.indent(1)
                ca.add("nodes2kill.append(old_di[i])")                

            ca.indent(-2)
            ca.add("for e in nodes2kill:")
            ca.indent(1)
            ca.add("old_di.remove(e)")
            ca.indent(-1)                    

            # add the new elements    
            ca.add("node_idx = start_idx")
            ca.add("for i in elements2be:")
            ca.indent(1)
            ca.add("node_idx += 1")
            ca.add("if existing.has_key(i):")
            ca.indent(1)
            ca.add("if node_idx < len(old_di):")
            ca.indent(1)
            ca.add("if old_di[node_idx].semanticModel.element != i:")
            ca.indent(1)
            ca.add("old_di.remove(existing[i])")
            ca.indent(-1)
            ca.add("else:")
            ca.indent(1)
            ca.add("continue")
            ca.indent(-3)
            ca.add(nodename + " = DiagramElementGenerator.getExtension(i.__type__()).createDiagramElement(i, xparent="+con+")")
            ca.add("old_di.insert(" + nodename + ", node_idx)")
            ca.add("")
            ca.indent(-1)



            # add new separators (we will want to reuse the old, don't we)
            if separator:
                ca.add("for i in xrange(len(elements2be)-1):")
                ca.indent(1)
                ca.add("sep = self.createSimpleGraphNode(\""+str(separator.name)+"\")")
                ca.add("old_di.insert(sep, start_idx+2*(i+1))")
                ca.indent(-1)
            ca.add("")

            ca.indent(-1)

        else: ## The property to synchronize is unordered
            # Kill dead graphnodes
            if not con:
                con="elementGN"
                oldCon="elementGN"

            startname, endname = "",""
            # Add start/end nodes if needed
            if len(path)>1:
                startname, endname = __getStartAndEnd(path[-1])

            first, prevnode, last, nextnode = __getSurroundingNodes(gn)

            #check for separator
            separator=__getSeparator(gn)

            ## 'old_di' is the collectin of the contained graphnodes
            ca.add("old_di="+con+".contained")



            ## Remove separators, start and endnodes and orphaned graphnodes
            ca.add("for xmidi in old_di:")
            ca.indent(1)
            ca.add("sm = xmidi.semanticModel")
            ca.add("if not hasattr(sm, \"typeInfo\") and sm.element is None:")
            ca.indent(1)
            ca.add("old_di.remove(xmidi)")
            ca.indent(-1)
            if startname and endname:
                ca.add("if hasattr(sm, \"typeInfo\") and sm.typeInfo in [\""+startname+"\",\""+endname+"\"]:")
                ca.indent(1)
                ca.add("old_di.remove(xmidi)")
                ca.indent(-1)
            if separator:
                ca.add("if hasattr(sm, \"typeInfo\") and sm.typeInfo == \""+str(separator.name)+"\":")
                ca.indent(1)
                ca.add("old_di.remove(xmidi)")
                ca.indent(-1)

            ca.indent(-1)

            parent = path[-1]
            childlinks = __getChildLinks(parent)
            list_names = []
            for c in childlinks:
                list_names.append(str( __getChild(c).name))
            q = 0
            for l in list_names:
                if l == "?":
                    q += 1

            synch_multiple_props = False
            if not len(childlinks) == 1:
                if q > 1: ## Case where we have several '?' fighting over the same container
                    ## This generates a thorough synchronization that checks all involved "?" nodes
                    c = __gen_xmidi_synch_multi(self, "element", parent, ca, metaclass, con,property)
                    synch_multiple_props = True
                ## All other cases should be possible to solve by similar means as with ordered slots

            if synch_multiple_props:
                ca.indent(-1)
                ca.add("")
                return

            
            if not (startname and endname):
                if first != 0:
                    ca.add("start_cont , start_idx = searchForSimpleGraphNode(old_di, \""+ str(prevnode.name) +"\")")
                else:
                    ca.add("start_idx = -1")

                # check position of the right bound-node
                if last != -1:
                    ca.add("end_cont , end_idx = searchForSimpleGraphNode(old_di, \""+ str(nextnode.name) +"\")")
                else:
                    ca.add("end_idx = len(old_di)")
            else:
                ca.add("start_idx = -1")
                ca.add("end_idx = len(old_di)")
            ca.add("existing = {}")
            ca.add("for xmidi in " + con+ ".contained:")
            ca.indent(1)
            ca.add("existing[xmidi.semanticModel.element] = xmidi")
            ca.indent(-1)
            ca.add('nodes = getListOfReparentedGNs(elementGN)')
            
            ca.add("node_idx = start_idx + 1")
            ca.add("for i in elements2be:")
            ca.indent(1)
            ca.add("if existing.has_key(i):")
            ca.indent(1)
            ca.add("node_idx += 1")
            ca.add("continue")
            ca.indent(-1)

            freelayout = False
            for attr in gn.feature:
                if attr.name=="freelayout":
                    freelayout = True
                    
            if freelayout:
                ca.add(nodename+" = None")
                ca.add('for n in nodes:')
                ca.indent(1)
                ca.add('cdi, x, y = n')
                ca.add('if cdi.semanticModel.element == i:')
                ca.indent(1)
                ca.add(nodename+' = cdi')
                ca.add('ps_x, ps_y = coral.XMIDI_GlobalToLocalPosition('+con+', x, y)')
                ca.add(nodename+'.position.x = ps_x')
                ca.add(nodename+'.position.y = ps_y')
                ca.add('break')
                ca.indent(-2)
                ca.add("if not "+nodename+":")
                ca.indent(1)
                ca.add("ext = DiagramElementGenerator.getExtension(i.__type__())")
                ca.add("if ext and ext.isMinorElement():")
                ca.indent(1)
                ca.add(nodename + " = ext.createDiagramElement(i, xparent="+con+")")
                ca.indent(-2)
                
            else: ## The default way to insert graphnodes in the compartment
                if str(gn.name) != "?":
                    ca.add(nodename + " = self.createGraphNode(i)")
                else:
                    ca.add("ext = DiagramElementGenerator.getExtension(i.__type__())")
                    ca.add("if ext:")
                    ca.indent(1)
                    ca.add(nodename + " = ext.createDiagramElement(i, xparent="+con+")")
                    ca.indent(-1)

            ca.add("if " +nodename+ ":" )
            ca.indent(1)


            # The associations tell us what child elements the GraphElement should have
            # This will become obsolete
            association_ends = __getChildLinks(gn)
            ss = selfstring
            i=0
            for ae in association_ends:
                childnode = __getChild(ae)
                if str(childnode.name) != "?":
                    ca.add("xmi_child_"+str(i)+" = self.createSimpleGraphNode(\""+childnode.name+ "\")")
                    ca.add("if xmi_child_"+str(i)+":")
                    ca.indent(1)
                    ca.add(nodename+".contained.append(xmi_child_"+str(i)+")")
                    ca.indent(-1)
                    i = i + 1

            # If parent (directly above) is a GraphEdge,
            # give a default position to children that are _not_ simple compartments etc.
            parent_node = __getParentElement(gn)

            if str(parent_node.name)=="GraphEdge":
                ca.add("if "+ con +".oclIsKindOf(DI.GraphEdge) and len("+con+".waypoints) >= 2:")
                ca.indent(1)
                ca.add(nodename+".position.x = ("+con+".waypoints[0].x+"+con+".waypoints[-1].x)/2")
                ca.add(nodename+".position.y = ("+con+".waypoints[0].y+"+con+".waypoints[-1].y)/2-20")
                ca.indent(-1)
            ## End default position-code

            ca.add("old_di.insert(" + nodename + ", node_idx )")
            ca.add("node_idx += 1")
            ca.indent(-2)

            # remove XMIDI, whose elements are deleted
            ca.add("for i in existing.keys():")
            ca.indent(1)
            ## Different removal strategy for slots 0..1 and 0..*

            if metaclass.getPropertyByName(property).getMultUpper() == 1:
                ca.add("if i is None or i==new_am:")
                ca.indent(1)
                ca.add("continue")
                ca.indent(-1)
                ca.add("if i in old_am:")
                ca.indent(1)
                ca.add("old_di.remove(existing[i])")
                ca.indent(-2)
            else:
                ca.add("if i is None or i in new_am:") ## Do not delete simple diagram elements
                ca.indent(1)
                ca.add("continue")
                ca.indent(-1)
                ca.add("if i in old_am and i not in new_am:")
                ca.indent(1)
                ca.add("old_di.remove(existing[i])")
                ca.indent(-2)

            ## Add start and endnodes and separators

            if separator:
                ca.add("for i in xrange(len(elements2be)-1):")
                ca.indent(1)
                ca.add('old_di.insert(self.createSimpleGraphNode("'+separator.name+'"),start_idx+2*(i+1))')
                ca.indent(-1)
            if startname and endname:
                ca.add("if len(old_di) != 0:")
                ca.indent(1)
                ca.add('old_di.insert(self.createSimpleGraphNode("'+startname+'"),0)')
                ca.add('old_di.append(self.createSimpleGraphNode("'+endname+'"))')
                ca.indent(-1)
                ca.add("else:")
                ca.indent(1)
                ca.add(oldCon+".contained.remove("+con+")")
                ca.indent(-1)
                    
            ca.indent(-1)
            ca.add("")


    ## Should the graphnode not be a childelement (indicated with a '?'), recurse to the next level
    
    # The associations tell us what child elements the GraphElement should have 
    association_ends = __getChildLinks(gn)
    ss = selfstring
    i=0
    for ae in association_ends:
        i += 1
        association = __getAssociation(ae)
        child = __getChild(ae)

        ## When we reach the top, reset path
        if (str(gn.name) in ["GraphNode","GraphEdge"]) and not synchronize:
            if not [a for a in gn.association if a.aggregation != "composite"]:
                path = [gn]
        else:
            if gn not in path and not synchronize:
                path.append(gn)

        for tv in association.taggedValue:
            # Get the information from the tagged value of the association
            synch_indent = 0            

            # continue if the child is a "?"
            synchronize=__synchronizeGraphNode(child)

            if str(child.name) == "?" or synchronize:
                # which property are we refering to
                sc = re.search("synchronizeconstraint:\s*(.*)", str(tv.dataValue))
                m = re.search("property:\s*(.*)", str(tv.dataValue))
                if m:
                    property = str(m.group(1))
                    propstr = 'if property == "' + property + '"'
                    if sc:
                        propstr = propstr + " and " + str(sc.group(1))
                    propstr = propstr + ':'
                    ca.add(propstr)
                    ca.indent(1)
                    synch_indent += 1
                    
                    ca.add("elements2be = []")
                    if metaclass.getPropertyByName(property).getMultUpper() == 1:
                        ca.add("if new_am:")
                        ca.indent(1)
                        synch_indent += 1
                        ca.add("child=new_am")
                    else:
                        ca.add("for child in new_am:")
                        ca.indent(1)
                        synch_indent += 1
                        
                    # check for childconstraint
                    m2 = re.search("childconstraint:\s*(.*)", str(tv.dataValue))
                    if m2:
                        ca.add("if "+str(m2.group(1))+":")
                        ca.indent(1)
                        synch_indent += 1
                            
                    # retrieve is a more specific constraint that tells us which elements should have a graphnode
                    retrievestring = re.search("retrieve:\s*(.*)", str(tv.dataValue))
                    if retrievestring:
                        ca.add("if child in "+str(retrievestring.group(1))+":")
                        ca.indent(1)
                        synch_indent += 1
                    ca.add("elements2be.append(child)")
                        
                    # check if the property is ordered or not
                    if metaclass.getPropertyByName(property).isOrdered():
                        isOrdered = True
                    else:
                        isOrdered = False
                        
            # reset the extra indentation
            ca.indent(-synch_indent)
        # collect information recursively      
        createSynchronizeContents(self, ss, child, ca, metaclass, property)

        ## Remove the last container when we have gone through all subtrees
        if i==len(association_ends) and gn==path[-1]:
            del path[-1]

def createAttachConnectors( gn, ca ):
    ca.add("def attachGraphConnector( self, element ):")
    ca.indent(1)
    # Collect the edges
    ca.add('edges = element.xmidi_get_graphelements()')
    ca.add("ends=[]")
    #ends=""
    # Collect the indexes
    for tv in gn.taggedValue:
        m = re.search("index1:\s*(.*)", str(tv.dataValue))
        if m:
            ca.add("ends += " + str(m.group(1)))
        m = re.search("index2:\s*(.*)", str(tv.dataValue))
        if m:
            ca.add("ends += " + str(m.group(1)))
    ca.add("for end in ends:")
    ca.indent(1)
    # add next_property if needed
    next_property=""
    for tv in gn.taggedValue:
        m = re.search("next_property:\s*(.*)", str(tv.dataValue))
        if m:
            next_property= str(m.group(1)) +"."
            
    # decide to which graphnodes we should connect the edges
    ca.add('classnodes = end.'+next_property+'xmidi_get_graphelements()')
    ca.add("for classnode in classnodes:")
    ca.indent(1)
    ca.add("cnt = False")
    # Check if the graphnode already has connectors for the edge
    ca.add("for a in classnode.anchorage:")
    ca.indent(1)
    ca.add("if a.graphEdge and a.graphEdge[0] in edges:")
    ca.indent(1)
    ca.add("cnt = True")
    ca.add("break")
    ca.indent(-2)
    # if so, don't add a new one
    ca.add("if cnt:")
    ca.indent(1)
    ca.add("continue")
    ca.indent(-1)
    ca.add("")
    # else add one
    ca.add("connector = DI.GraphConnector(graphElement=classnode, position=DI.Point())")
    ca.add("classnode.anchorage.append(connector)")
    ca.add("")
    # Check if edge already has connectors
    ca.add("for edge in edges:")
    ca.indent(1)
    # if so, do nothing
    ca.add("if len(edge.anchor) == 2:")
    ca.indent(1)
    ca.add("continue")
    ca.indent(-1)
    # else add connector
    ca.add("edge.anchor.append(connector)")
    ca.indent(-4)
    ca.add("")

    
def createSynchronizeDiagram( self, selfstring, dep, ca, metaclass):
    ## If a diagram dependency 'dep' is passed, a diagram synchronization
    ## will occur for the element
    if dep and dep.name=="DiagramDependency":
        ca.indent(-2)
        ca.add("if diagrams:")
        ca.indent(1)
        for tv in dep.taggedValue:
            # Get the information from the tagged value of the dependency
            # which property are we refering to
            m = re.search("property:\s*(.*)", str(tv.dataValue))
            if m:
                ca.add('if property =="' + str(m.group(1)) + '":')
                ca.indent(1)
            ca.add("elements2be = []")
            ca.add("for child in new_am:")
            ca.indent(1)
            # check for childconstraint
            m2 = re.search("childconstraint:\s*(.*)", str(tv.dataValue))
            if m2:
                ca.add("if "+str(m2.group(1))+":")
                ca.indent(1)
            ca.add("elements2be.append(child) ")
            ca.indent(-2)
            ca.add("for d in diagrams:")
            ca.indent(1)
            ca.add("nodes = getListOfReparentedGNs(d)")
            ca.add("existing = {}")
            ca.add("for di in d.contained:")
            ca.indent(1)
            ca.add("existing[di.semanticModel.element] = di")
            ca.indent(-1)            
            ca.add("for i in elements2be:")
            ca.indent(1)
            ca.add("if existing.has_key(i):")
            ca.indent(1)
            ca.add("continue")
            ca.indent(-1)
            ca.add("ge=None")
            ca.add("for n in nodes:")
            ca.indent(1)
            ca.add("cdi, x, y = n")
            ca.add("if cdi.semanticModel.element == i:")
            ca.indent(1)
            ca.add("ge = cdi")
            ca.add("ge.position.x = x")
            ca.add("ge.position.y = y")
            ca.indent(-1)
            ca.add("if ge:")
            ca.indent(1)
            ca.add("d.contained.append(ge)")
            ca.indent(-2)
            ca.add("if not ge:")
            ca.indent(1)
            ca.add("createit = False")
            ca.add("gns = i.xmidi_get_graphelements()")
            ca.add("for gn in gns:")
            ca.indent(1)
            ca.add("if gn.__getRoot__() != d:")
            ca.indent(1)
            ca.add("createit = True")
            ca.indent(-2)
            ca.add("if createit or not gns:")
            ca.indent(1)
            ca.add("ext = DiagramElementGenerator.getExtension(i.__type__())")
            ca.add("if ext and ext.isMinorElement():")
            ca.indent(1)
            ca.add("ge = ext.createDiagramElement(i, xparent=d)")
            ca.indent(-1)
            ca.add("if ge:")
            ca.indent(1)
            ca.add("if ge.oclIsKindOf(DI.GraphNode):")
            ca.indent(1)
            ca.add("ge.position.x = 50")
            ca.add("ge.position.y = 50")
            ca.indent(-1)
            ca.add("d.contained.append(ge)")
            ca.indent(-5)
                            

def createAttachToParent(gn, ca):
    for attr in gn.feature:
        if attr.name=="attachToParent":
            ca.add("def createAndAttachToParent(self, element, parent_xmidi, **kwargs):")
            ca.indent(1)
            ca.add("p = element.__parent__()")
            for tv in gn.taggedValue:
                m = re.search("parentconstraint:\s*(.*)", str(tv.dataValue))
                if m:
                    ca.add("if p and " + str(m.group(1)) + ":")
                    ca.indent(1)
                else:
                    ca.add("if p:")
                    ca.indent(1)
            ca.add("raise IgnoreThisXMIDICreation")
            ca.indent(-1)
            ca.add("return DiagramElementCreatorExt.createAndAttachToParent(self, element, parent_xmidi, **kwargs)")
            ca.indent(-1)
            ca.add("")

  
def gen_xmidi(element):
    global perpetual, importLanguage, ca
    ## assert len(element.clientDependency.supplier) > 0
    guards_no_tv = [i for i in element.clientDependency if len(i.taggedValue)==0]
    assert len(guards_no_tv) <= 1, "Not allowed to have many optional variations of craetion without guards"
    guards_with_tv = [i for i in element.clientDependency if len(i.taggedValue)==1]
    guards_with_tv.sort(lambda x,y: cmp(x.taggedValue[0].name, y.taggedValue[0].name))
    diagram_dep = None
    for g in guards_with_tv:
        if str(g.name) == "DiagramDependency":
            diagram_dep = g
            break

    if diagram_dep:
        guards_with_tv.remove(diagram_dep)
        
    guards = guards_with_tv + guards_no_tv
    assert len(guards) > 0
    ## top = element.clientDependency.supplier[0]
    ##ca = CodeArtifact()
    ##ca.setIndentString("    ")
    
    # Get the language
    m=re.search("(\w+)(\d)(\d)::\s*(.*)",str(element.name))
    assert m
    lang = m.group(1)
    ver = m.group(2)
    rev = m.group(3)
    L = coral.Language.__getLanguage__(lang, int(ver), int(rev))
    classname=m.group(4)
    metaclass = L.getElement(classname)

    # Import the language only the first time this is run
    if importLanguage:
        ca.add("coral.lang."+str(lang)+str(ver)+str(rev)+".__import__(globals())")
        coral.lang.UML14.__import__(globals())
        ca.add("")
        importLanguage = False

    # the name of the class
    ca.add("class "+classname+"DEG( DiagramElementCreatorExt ):")
    ca.add("")
    ca.indent(1)
    
    # begin createDiagramElement
    ca.add("def createDiagramElement( self, element, xparent=None, **kwargs):")
    ca.indent(1)
    ca.add("xparent_element = findUpwardsModelElement(xparent)")
    if len(guards) == 1 and len(guards[0].taggedValue)==0:
        top = guards[0].supplier[0]
        ## Only one variation of creation
        the_ss = __gen_xmidi(element, "element", top, ca, metaclass, "None")
        ca.add("return "+the_ss)
        ca.indent(-1)
        ca.add("")
        perpetual = 1
    else:
        first = 0
        ## Several options 
        for g in guards:
            condition = ""
            for tv in g.taggedValue:
                m = re.search("^guard:\s*(.*)",str(tv.dataValue))
                condition = m.group(1)
            if condition:
                top = g.supplier[0]
                if first == 0:
                    ca.add("if "+condition+":")
                else:
                    ca.add("elif "+condition+":")
                ca.indent(1)
                the_ss=__gen_xmidi(element, "element", top, ca, metaclass, "None")
                ca.add("return "+the_ss)
                ca.indent(-1)
            else:
                top = g.supplier[0]
                ca.add("else:")
                ca.indent(1)
                the_ss=__gen_xmidi(element, "element", top, ca, metaclass, "None")
                ca.add("return "+the_ss)
                ca.indent(-1)
            first += 1
            perpetual = 1
        ca.indent(-1)
        ca.add("")
    # end createDiagramElement

    # begin isMinorElement
    ca.add("def isMinorElement( self, element=None, xparent=None ):")
    ca.indent(1)
    createIsMinorElement(element, ca)
    ca.indent(-1)
    ca.add("")
    # end isMinorElement

    # begin suitableDiagrams
    createSuitableDiagrams(element, ca)
    # end suitableDiagrams

    # begin synchronizeContents
    ca.add("def synchronizeContents(self, element, property, old_am, new_am):")
    ca.indent(1)
    ca.add('elementGNs = element.xmidi_get_graphelements()')
    ca.add('diagrams = element.xmidi_get_diagrams()')
    noXMI = False
    for g in guards:
        node = g.supplier[0]
        for attr in node.feature:
            if attr.name=="noXMI":
                noXMI=True
    if noXMI:
        ca.add("by_new_am = False")
        ca.add("if not elementGNs and new_am:")
        ca.indent(1)
        ca.add("by_new_am = True")
        ca.add("elementGNs = new_am.xmidi_get_graphelements()")
        ca.indent(-1)
    ca.add("if len(elementGNs)>0:")
    ca.indent(1)
    ca.add("for elementGN in elementGNs:")
    ca.indent(1)
    ca.add("xparent = elementGN.container")
    ca.add("xparent_element = findUpwardsModelElement(xparent)")
    ca.add("")
    if len(guards) == 1 and len(guards[0].taggedValue)==0:
        ## One way to synchronize
        createSynchronizeContents( element, "element", top, ca, metaclass)
        if diagram_dep:
            createSynchronizeDiagram( element, "element", diagram_dep, ca, metaclass)
        ca.indent(-3)
        ca.add("")
    else:
        first = 0
        ## Several ways to synchronize
        for g in guards:
            condition = ""
            for tv in g.taggedValue:
                m = re.search("^guard:\s*(.*)",str(tv.dataValue))
                condition = m.group(1)
            if condition:
                top = g.supplier[0]
                if first == 0:
                    ca.add("if "+condition+":")
                else:
                    ca.add("elif "+condition+":")
                ca.indent(1)
                ca.add("pass") ## This is ugly, but we might not need to do anything in the synchronisation
                createSynchronizeContents( element, "element", top, ca, metaclass)
            else:
                top = g.supplier[0]
                ca.add("else:")
                ca.indent(1)
                ca.add("pass")
                createSynchronizeContents( element, "element", top, ca, metaclass)
            ca.indent(-1)
            first += 1

        if diagram_dep:
            createSynchronizeDiagram( element, "element", diagram_dep, ca, metaclass)

        ca.indent(-3)
        ca.add("")
    # end synchronizeContents

    # begin attachConnectors
    node = None
    for g in guards:
        node = g.supplier[0]
        
    if node.name=="GraphEdge":
        createAttachConnectors(node, ca)
    # end attachConnectors

    # begin createAndAttachToParent
    createAttachToParent(element,ca)
    # end createAndAttachToParent
    ca.indent(-1)
    perpetual = 1
    return ca


def usage():
    print "Usage: uml2xmidigen [-o outputfile] inputmodels"
    print "    inputmodels: the name(s) of your MODEL2XMIDI model(s)"
    print "options:\n -h, --help : display this help"
    print "-o \"outputfile\": the filename for the output" 

def main(arguments):
    global ca
    try:
        opts, args = getopt.gnu_getopt(arguments, "ho:", ["help"])
        
    except getopt.GetoptError:
        # print help information and exit:
        usage()
        sys.exit(2)
    if len(args) < 1:
        usage()
        sys.exit(2)

    output = None

    for o, a in opts:
        if o in ("-h", "--help"):
            usage()
            sys.exit()
        if o in ("-o"):
            output = a
            
    inputmodels = args

    
    ca = CodeArtifact()
    ca.setIndentString("    ")
    ca.add("# -*- coding: utf-8 -*-")
    ca.add("## This file is generated by uml2xmidigen")
    ca.add("## All changes made in this file will be lost")
    ca.add("")    
    ca.add("from coral.modeler.diagram.degen import DiagramElementCreatorExt, CannotAttachChildException, IgnoreThisXMIDICreation")
    ca.add("from coral.modeler.diagram.degen import DiagramElementGenerator, searchForSimpleGraphNode, geInDiagram, getListOfReparentedGNs")
    ca.add("import coral")
    ca.add("DI = coral.lang.XMIDI20")
    ca.add("from coral.modeler.diagram.di_support import findUpwardsModelElement")
    ca.add("from coral import log")
    ca.add('logger=log.getLogger("UML2DI Degen")')

    ca.add("import coral")

    UML14 = coral.lang.UML14

    # Then create DEG:s for all the classes in the models
    for modelfile in inputmodels:

        umlclasses=[]

        st = coral.loadDocument(modelfile)
        model = st.contents().select(lambda x: x.__type__().__language__() == UML14)[0]

        umlclasses = model.__getAllParts__().select(lambda x: x.__type__() == UML14.Class and len(x.clientDependency) and len(x.taggedValue) == 1 and re.search("^init\s*$", str(x.taggedValue[0].dataValue)))

        listOfClasses =[]
        for umlclass in umlclasses:
            listOfClasses.append(umlclass)
        listOfClasses.sort(__comparefunction)

        for umlclass in listOfClasses:
            gen_xmidi(umlclass)

    # Save output to specified file
    if output:
        ca.dump(output)
    else:
        ca.dump_fd(sys.stdout)


if __name__=='__main__':
    import sys
    main(sys.argv[1:])
