// **************************************************************
// The contents of this file are copyright Ceteva Limited and
// should not be redistributed in any form whatsoever without the
// express written consent of Ceteva Limited.
// *************************************************************
// A Parser for UML 1.1 XMI exported by Rational Rose
parserImport XML::Parser;
parserImport XOCL;
import XML;
import Parser;
import XMI;
// A wrapper for generalisations
context XMI11Rose
@Class Gen
@Attribute parent : Element end
@Constructor(parent) end
end
// A wrapper for tagged values
context XMI11Rose
@Class TaggedValue
@Attribute modelElement : Element end
@Attribute tag : String end
@Attribute value : Element end
@Constructor(modelElement,value) end
end
context XMI11Rose
@Operation grammar()
@Grammar XMI11Rose
XMI11Rose ::= [ Header ] model = Content { model }.
Header ::= ANY* .
Content ::= defs = Def*
{ defs }.
Def ::= Model | TaggedValue | ANY {"Unknown" }.
Model ::=
owned = Owned
id := {owned->select(o |
o.isReallyKindOf(XCore::Class) or
o.isReallyKindOf(XCore::Package) or
o.isReallyKindOf(Associations::Association))->
iterate(o p = XCore::Package(name.subst("_"," ",true)) | p.add(o))}.
Package ::=
owned = (Owned | {Seq{}})
id := {owned->select(o |
o.isReallyKindOf(XCore::Class) or
o.isReallyKindOf(XCore::Package) or
o.isReallyKindOf(Associations::Association))->
iterate(o p = XCore::Package(name.subst("_"," ",true)) | p.add(o))}.
Owned ::=
elements = Element*
{ elements }.
Element ::= DataType | Class | Package | Association | Gen | Interface | AssociationClass | ANY {null} .
DataType ::=
id := {
@Case name of
"Boolean" do XCore::Boolean end
"Integer" do XCore::Integer end
"int" do XCore::Integer end
"String" do XCore::String end
else XCore::Class(name)
end
}.
Class ::=
owned = (Owned | { Seq{} })
features = (ClassifierFeature | { Seq{} })
id := {
(owned->reject(o | o.isKindOf(Gen)) + features)->iterate(f c =
XCore::Class[name = Symbol(name.subst("_"," ",true)),
parents = owned->select(g | g.isKindOf(Gen))->collect(g | g.parent)->asSet,
isAbstract = if isAbstract = "true"
then true
else false end] | c.add(f))}.
AssociationClass ::=
//-------------------------
connection = Connection*
owned = Owned*
features = ClassifierFeature*
//-------------------------
id := {XCore::Class[name = Symbol(name.subst("_"," ",true))]}.
Interface ::=
id := {XCore::Class[name = Symbol(name.subst("_"," ",true))]}.
AssociationClass ::=
id := {XCore::Class[name = Symbol(name.subst("_"," ",true))]}.
Gen ::=
{Gen(XML::Parser::Ref(parent))}.
OptClassifierFeature ::= ClassifierFeature | { Seq{} }.
ClassifierFeature ::=
feature = Feature*
{feature}.
Feature ::= Attribute | Operation.
Attribute ::=
FeatureMultiplicity
InitialValue
{XCore::Attribute(name.subst("_"," ",true),XML::Parser::Ref(type))}.
FeatureMultiplicity ::=
ANY
.
InitialValue ::=
ANY
.
Operation ::=
ANY*
{@Operation() self end.setName(name.subst("_"," ",true))}.
Association ::=
connection = Connection
id := {
let a = Associations::Association(if name = "" then id else name.subst("_"," ",true) end);
end1 = connection->at(0)->head;
end2 = connection->at(1)->head
in
// Store association names and ends in a table so that
// we can run over the associations at the end and
// turn them into attributes if they are navigable in one direction
XMI11Rose::navigableAssocs.put(a.name->toString(),Seq{connection->at(0),connection->at(1)});
// If the ends don't have a name set it to a default name
if end1.name.toString() = "" then end1.setName("end1") end;
a.setEnd1(end1);
if end2.name.toString() = "" then end2.setName("end2") end;
a.setEnd2(end2)
end}.
Connection ::=
ends = End*
{ends}.
End ::=
endmult = EndMultiplicity
id := {
Seq{Associations::End(name.subst("_"," ",true),XML::Parser::Ref(type),endmult),if isNavigable.toString() = "true" then true else false end}}.
EndMultiplicity ::=
mult = Multiplicity
{mult}.
Multiplicity ::=
range1 = (Range1 | {Associations::One()})
{range1}.
Range1 ::=
range2 = Range2
{range2}.
Range2 ::=
{
if lower = "1" and upper = "1" then
Associations::One()
elseif lower = "0" and upper = "n" then
Associations::Star()
elseif lower = "0" and upper = "1" then
Associations::Opt()
elseif lower = "1" and upper = "n" then
Associations::Mandatory()
else
Associations::Star()
end}.
Classifier ::= {id}.
TaggedValue ::=
v = (Value | {null})
{TaggedValue(XML::Parser::Ref(modelElement),tag,if v = null then value else v end)}.
Value ::=
value = TEXT
{value}.
end
end
context XMI11Rose
@Operation loadRoseXMI11(file)
let g = grammar().compile()
in @WithOpenFile(fin <- file)
let xin = ParserChannel(fin,g)
in
Root::g := g;
// Set this to true if you want to
// get a debug trace in the console
xin.debug := false;
xmf.busy("Parsing XMI");
xin.parse("XMI11Rose")
end
end
end
end
context XMI11Rose
// Bind a table for storing navigable association
@Bind navigableAssocs = Table(1000) end
context XMI11Rose
@Operation importRoseXMI11(p)
let i = xmf.openFile(xmf.homeDir(),"*.xmi") in
if i <> "" then
let
data = loadRoseXMI11(i) then
model = data->head then
taggedvalues = data->tail->select(t | t.isKindOf(TaggedValue)) in
xmf.killProgressDialogs();
// Add the model to root and initialise it
Root.add(model);
model.init();
// Deal with data values
@For t in taggedvalues do
if t.tag = "description" then
t.modelElement.setDoc(t.value)
end
end;
// Deal with association ends that did't have a name. Turn them into the
// lower case name of the end's classifier
let associations = model->allContentsOf(Associations::Association) in
@For a in associations do
if a.end1.name.toString() = "end1" or a.end1.name.toString() = "end2" then
a.end1.setName(a.end1.underlyingType().name.lowerCaseInitialLetter())
end;
if a.end2.name.toString() = "end1" or a.end2.name.toString() = "end2" then
a.end2.setName(a.end2.underlyingType().name.lowerCaseInitialLetter())
end
end;
// Initialise the associations as model.init() currently doesn't do it
@For a in associations do
a.init()
end;
// Run over the associations and remove any that are navigable in one direction.
// Create an attribute for each one.
@For a in associations do
let ends = XMI11Rose::navigableAssocs.get(a.name.toString())
in if ends->head->last = true and ends->last->last = false then
let att = XCore::Attribute(ends->head->head.name.toString(),ends->head->head.type)
in
ends->last->head.underlyingType().add(att);
a.remove();
a.owner.remove(a)
end
end;
if ends->head->last = false and ends->last->last = true then
let att = XCore::Attribute(ends->last->head.name.toString(),ends->last->head.type)
in
ends->last->head.underlyingType().add(att);
a.remove();
a.owner.remove(a)
end
end
end
end;
p.add(Projects::Project(model,model.name,null))
end
end
end
end
end