/* * (c) Copyright 2010-2011 AgileBirds * * This file is part of OpenFlexo. * * OpenFlexo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenFlexo is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenFlexo. If not, see <http://www.gnu.org/licenses/>. * */ package cb.xmi; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.StringTokenizer; import ru.novosoft.uml.behavior.use_cases.MActorImpl; import ru.novosoft.uml.behavior.use_cases.MUseCaseImpl; import ru.novosoft.uml.foundation.core.MAbstraction; import ru.novosoft.uml.foundation.core.MAbstractionImpl; import ru.novosoft.uml.foundation.core.MAssociation; import ru.novosoft.uml.foundation.core.MAssociationClassImpl; import ru.novosoft.uml.foundation.core.MAssociationEnd; import ru.novosoft.uml.foundation.core.MAssociationEndImpl; import ru.novosoft.uml.foundation.core.MAssociationImpl; import ru.novosoft.uml.foundation.core.MAttribute; import ru.novosoft.uml.foundation.core.MAttributeImpl; import ru.novosoft.uml.foundation.core.MClassImpl; import ru.novosoft.uml.foundation.core.MClassifier; import ru.novosoft.uml.foundation.core.MComponentImpl; import ru.novosoft.uml.foundation.core.MConstraint; import ru.novosoft.uml.foundation.core.MConstraintImpl; import ru.novosoft.uml.foundation.core.MDataTypeImpl; import ru.novosoft.uml.foundation.core.MDependency; import ru.novosoft.uml.foundation.core.MDependencyImpl; import ru.novosoft.uml.foundation.core.MGeneralization; import ru.novosoft.uml.foundation.core.MGeneralizationImpl; import ru.novosoft.uml.foundation.core.MInterfaceImpl; import ru.novosoft.uml.foundation.core.MModelElement; import ru.novosoft.uml.foundation.core.MOperation; import ru.novosoft.uml.foundation.core.MOperationImpl; import ru.novosoft.uml.foundation.core.MParameter; import ru.novosoft.uml.foundation.core.MParameterImpl; import ru.novosoft.uml.foundation.core.MUsage; import ru.novosoft.uml.foundation.core.MUsageImpl; import ru.novosoft.uml.foundation.data_types.MAggregationKind; import ru.novosoft.uml.foundation.data_types.MBooleanExpression; import ru.novosoft.uml.foundation.data_types.MCallConcurrencyKind; import ru.novosoft.uml.foundation.data_types.MExpression; import ru.novosoft.uml.foundation.data_types.MMultiplicity; import ru.novosoft.uml.foundation.data_types.MParameterDirectionKind; import ru.novosoft.uml.foundation.data_types.MVisibilityKind; import ru.novosoft.uml.foundation.extension_mechanisms.MStereotype; import ru.novosoft.uml.foundation.extension_mechanisms.MStereotypeImpl; import ru.novosoft.uml.model_management.MModel; import ru.novosoft.uml.model_management.MModelImpl; import ru.novosoft.uml.model_management.MPackage; import ru.novosoft.uml.model_management.MPackageImpl; import ru.novosoft.uml.model_management.MSubsystemImpl; import cb.petal.AccessQualified; import cb.petal.Association; import cb.petal.ClassAttribute; import cb.petal.ClassCategory; import cb.petal.ClassUtility; import cb.petal.DependencyRelationship; import cb.petal.InheritanceRelationship; import cb.petal.Module; import cb.petal.Operation; import cb.petal.PetalFile; import cb.petal.RealizeRelationship; import cb.petal.Relationship; import cb.petal.Role; import cb.petal.StereoTyped; import cb.petal.SubSystem; import cb.petal.UseCase; import cb.petal.UsesRelationship; /** * Factory for classes, methods, etc., it also contains methods to add relationships, like uses/realize relationships. * * @version $Id: XMIFactory.java,v 1.2 2011/09/12 11:47:01 gpolet Exp $ * @author <A HREF="mailto:markus.dahm@berlin.de">M. Dahm</A> */ public class XMIFactory { protected PetalFile tree; protected XMIGenerator gen; protected XMIFactory() { } protected XMIFactory(PetalFile tree, XMIGenerator gen) { this.tree = tree; this.gen = gen; } public MModel createModel() { MModel model = new MModelImpl(); model.setName(tree.getModelName()); return model; } private HashMap stereo_types = new HashMap(); // Map <String, stereotype> protected final MStereotype getStereotype(String stereo) { MStereotype s = (MStereotype) stereo_types.get(stereo); if (s == null) { s = new MStereotypeImpl(); s.setName(stereo); gen.getPackage().addOwnedElement(s); stereo_types.put(stereo, s); } return s; } public MPackage createPackage(ClassCategory cat) { MPackage pack = new MPackageImpl(); pack.setName(cat.getNameParameter()); pack.setUUID(cat.getQuid()); return pack; } public MPackage createPackage(SubSystem sys) { MPackage pack = new MSubsystemImpl(); pack.setName(sys.getNameParameter()); pack.setUUID(sys.getQuid()); return pack; } protected void setStereotype(StereoTyped s, MModelElement e) { String stereo = s.getStereotype(); if (stereo != null) { e.setStereotype(getStereotype(stereo)); } } protected void setVisibility(AccessQualified a, MModelElement e) { String acc = a.getExportControl(); if (acc == null) { acc = "public"; } else { acc = acc.toLowerCase(); } MVisibilityKind kind = MVisibilityKind.forName(acc); if (kind == null) { throw new RuntimeException("Can't map access qualifier: " + acc); } e.setVisibility(kind); } protected void setConcurrency(Operation op, MOperation o) { String acc = op.getConcurrency(); if (acc == null) { acc = "sequential"; } else { acc = acc.toLowerCase(); } MCallConcurrencyKind kind = MCallConcurrencyKind.forName(acc); if (kind == null) { throw new RuntimeException("Can't map concurrency: " + acc); } o.setConcurrency(kind); } /** * @return MInterface, MClass, MAssociationClass or MActor */ public MClassifier createClass(cb.petal.Class clazz) { MClassifier cl; /* ArgoUML/Poseidon can't display/use Actors/AssociationClass correctly */ if (clazz.isInterface()) { cl = new MInterfaceImpl(); } else if (clazz.isActor()) { cl = new MActorImpl(); } else if (clazz.isAssociationClass()) { cl = new MAssociationClassImpl(); } else { cl = new MClassImpl(); } cl.setName(clazz.getNameParameter()); cl.setUUID(clazz.getQuid()); setVisibility(clazz, cl); if (clazz instanceof ClassUtility) { cl.setStereotype(getStereotype("utility")); } else if (!(clazz.isInterface() || clazz.isActor())) { setStereotype(clazz, cl); } return cl; } public MClassifier createUseCase(UseCase caze) { MClassifier cl = new MUseCaseImpl(); cl.setName(caze.getNameParameter()); cl.setUUID(caze.getQuid()); setStereotype(caze, cl); return cl; } public MClassifier createComponent(Module module) { MClassifier cl = new MComponentImpl(); cl.setName(module.getNameParameter()); cl.setUUID(module.getQuid()); setStereotype(module, cl); return cl; } public MClassifier createSubSystem(SubSystem sys) { MClassifier cl = new MSubsystemImpl(); cl.setName(sys.getNameParameter()); cl.setUUID(sys.getQuid()); setStereotype(sys, cl); return cl; } /** * Create classifier for data type not directly contained in the model such as attribute types like "int". */ public MClassifier createBasicType(String name) { MClassifier clazz = new MDataTypeImpl(); clazz.setName(name); clazz.setStereotype(getStereotype("utility")); return clazz; } /** * Look for classifier in model or create a new data type. */ protected MClassifier getClassifierFor(String type) { MClassifier clazz = (MClassifier) gen.searchElement(type, MClassifier.class); if (clazz == null) { clazz = createBasicType(type); gen.getModel().addOwnedElement(clazz); } return clazz; } public MAttribute createAttribute(ClassAttribute attr) { MAttribute a = new MAttributeImpl(); a.setName(attr.getNameParameter()); a.setUUID(attr.getQuid()); setVisibility(attr, a); setStereotype(attr, a); String type = attr.getType() == null ? "int" : attr.getType(); a.setType(getClassifierFor(type)); String init = attr.getInitialValue(); if (init != null) { a.setInitialValue(new MExpression("Java", init)); } return a; } public MOperation createOperation(Operation op) { MOperation m = new MOperationImpl(); m.setName(op.getNameParameter()); m.setUUID(op.getQuid()); setVisibility(op, m); setStereotype(op, m); setConcurrency(op, m); String type = op.getResult() == null ? "void" : op.getResult(); MParameter ret = new MParameterImpl(); ret.setName("return"); ret.setType(getClassifierFor(type)); ret.setKind(MParameterDirectionKind.RETURN); m.addParameter(ret); ArrayList params = new ArrayList(); if (op.getParameters() != null) { for (Iterator i = op.getParameters().getElements().iterator(); i.hasNext();) { cb.petal.Parameter p = (cb.petal.Parameter) i.next(); String name = p.getNameParameter(); type = p.getType(); // Typical erroneous "Address a", needs to be split if (type == null) { int index = name.indexOf(' '); if (index > 0) { type = name.substring(0, index); name = name.substring(index + 1).trim(); } else { type = "int"; } } MParameter param = new MParameterImpl(); param.setName(name); param.setType(getClassifierFor(type)); param.setKind(MParameterDirectionKind.IN); m.addParameter(param); } } return m; } private void setupRelationship(MDependency g, Relationship rel) { MClassifier client = gen.getContainingClassifier(rel); MClassifier supplier = gen.getClassifier(rel.getQuidu()); g.addSupplier(supplier); g.addClient(client); g.setUUID(rel.getQuid()); setStereotype(rel, g); if (rel.getLabel() != null) { g.setName(rel.getLabel()); } } public MGeneralization createGeneralization(InheritanceRelationship rel) { MGeneralization g = new MGeneralizationImpl(); MClassifier client = gen.getContainingClassifier(rel); MClassifier supplier = gen.getClassifier(rel.getQuidu()); g.setParent(supplier); g.setChild(client); g.setUUID(rel.getQuid()); return g; } public MAbstraction createRealization(RealizeRelationship rel) { MAbstraction g = new MAbstractionImpl(); setupRelationship(g, rel); g.setStereotype(getStereotype("realize")); return g; } public MUsage createUsage(UsesRelationship rel) { MUsage g = new MUsageImpl(); setupRelationship(g, rel); return g; } public MDependency createDependency(DependencyRelationship rel) { MDependency g = new MDependencyImpl(); setupRelationship(g, rel); return g; } private static String map(String number) { if ("n".equals(number.toLowerCase()) || "*".equals(number)) { return "" + MMultiplicity.N; } else { return number; } } protected MMultiplicity getMultiplicityFor(Role role) { int from = 1, to = 1; if (role.getClientCardinality() != null) { String card = role.getClientCardinality().getStringValue(); StringTokenizer tok = new StringTokenizer(card, ".,"); try { from = Integer.parseInt(map(tok.nextToken())); if (tok.hasMoreTokens()) { to = Integer.parseInt(map(tok.nextToken())); } else { to = from; } } catch (Exception e) { throw new RuntimeException("Invalid cardinality: " + card); } } else { Role other = role.getOtherRole(); if (other.isAggregate()) { from = 0; to = MMultiplicity.N; } } if (from == MMultiplicity.N) { from = 0; } return new MMultiplicity(from, to); } protected MAssociationEnd getRoleFor(Role role) { MClassifier clazz = gen.getClassifier(role.getQuidu()); MAssociationEnd ae = new MAssociationEndImpl(); ae.setType(clazz); ae.setNavigable(role.isNavigable()); ae.setMultiplicity(getMultiplicityFor(role)); if (!role.getRoleName().startsWith("$")) { ae.setName(role.getRoleName()); } if (role.isAggregation()) { ae.setAggregation(MAggregationKind.AGGREGATE); } else if (role.isComposition()) { ae.setAggregation(MAggregationKind.COMPOSITE); } else { ae.setAggregation(MAggregationKind.NONE); } String c = role.getConstraints(); if (c != null) { MConstraint constr = new MConstraintImpl(); MBooleanExpression body = new MBooleanExpression("Java", c); constr.setBody(body); ae.addConstraint(constr); gen.getPackage().addOwnedElement(constr); } return ae; } protected void setupAssociation(Association assoc, MAssociation a) { Role first = assoc.getFirstRole(); Role second = assoc.getSecondRole(); a.addConnection(getRoleFor(first)); a.addConnection(getRoleFor(second)); } public MAssociation createAssociation(Association assoc) { MAssociation a = new MAssociationImpl(); if (!assoc.getNameParameter().startsWith("$")) { a.setName(assoc.getNameParameter()); } setupAssociation(assoc, a); return a; } /*************************** Utility method *******************************/ /** * Convert Rose identifier to normal one. */ protected String makeName(String name) { char[] chars = name.toCharArray(); StringBuffer buf = new StringBuffer(); for (int i = 0; i < chars.length; i++) { char ch = chars[i]; if (ch == ':' && chars[i + 1] == ':') { buf.append('.'); i++; } else { if (Character.isLetterOrDigit(ch)) { buf.append(ch); } else { buf.append('_'); } } } return buf.toString(); } /** * Convert fully qualified rose name (with "foo::bar") * * @return tuple (class name, package name) */ protected String[] makeNames(String qual) { String name, pack; int index = qual.indexOf("::"); if (index < 0) { // weird ... pack = ""; name = makeName(qual); } else { int index2 = qual.lastIndexOf("::"); if (index == index2) { pack = ""; } else { pack = makeName(qual.substring(index + 2, index2)); } name = qual.substring(index2 + 2); } return new String[] { name, pack }; } }