/* * This software is Copyright 2005,2006,2007,2008 Langdale Consultants. * Langdale Consultants can be contacted at: http://www.langdale.com.au */ package au.com.langdale.xmi; import java.util.Iterator; import org.apache.xerces.util.XMLChar; import au.com.langdale.kena.ModelFactory; import au.com.langdale.kena.OntModel; import au.com.langdale.kena.OntResource; import au.com.langdale.kena.ResIterator; import com.hp.hpl.jena.graph.FrontsNode; import com.hp.hpl.jena.graph.Node; import com.hp.hpl.jena.graph.Triple; import com.hp.hpl.jena.vocabulary.OWL; import com.hp.hpl.jena.vocabulary.RDF; import com.hp.hpl.jena.vocabulary.RDFS; import com.hp.hpl.jena.vocabulary.XSD; /** * A translator which produces a OWL model with CIM/XML standard naming from * a OWL model derived from CIM UML. */ public class Translator implements Runnable { private OntModel model; private OntModel result; private String defaultNamespace; private boolean extraDecoration; private boolean uniqueNamespaces; /** * Construct from input model and the namespace for renamed resources. * * @param input the model to be translated. */ public Translator(OntModel model, String namespace, boolean usePackageNames ) { this.model = model; OntResource ont = model.getValidOntology(); if( ont != null ) { defaultNamespace = ont.getURI() + "#"; uniqueNamespaces = true; } else { defaultNamespace = namespace; uniqueNamespaces = usePackageNames; extraDecoration = true; } } /** * @return Returns the result model. */ public OntModel getModel() { return result; } /** * Apply translation to input model, populating the result model. * * Each statement in the input model is transcribed to the * result model with some resources substituted. */ public void run() { pass1.run(); System.out.println("Stage 2 XMI model size: " + getModel().size()); propagateAnnotation(UML.baseuri); pass2.run(); } private abstract class Pass implements Runnable { protected abstract FrontsNode renameResource(OntResource r, String l); /** * Pass over every statement and apply renameResource() to each resource. */ public void run() { result = ModelFactory.createMem(); Iterator it = model.getGraph().find(Node.ANY, Node.ANY, Node.ANY); while( it.hasNext()) { Triple s = (Triple) it.next(); add( renameResource(model.createResource(s.getSubject())), renameResource(model.createResource(s.getPredicate())), renameObject(s.getObject())); } model = result; } /** * Substitute a single node. * * If the node is a resource then it is substituted as * required. If the node is a literal it is preserved. */ protected Node renameObject(Node n) { if( n.isLiteral()) { return n; } else { FrontsNode r = renameResource(model.createResource(n)); return r == null? null: r.asNode(); } } /** * Substitute a a single resource. * * @param r the resource to be (possibly) replaced. * @return the resource that should appear in result * or null if the resource should not appear in the result. */ protected FrontsNode renameResource(OntResource r) { if (r.isAnon()) return r; if (!r.getNameSpace().equals(XMI.NS)) return r; String ls = r.getString(RDFS.label); if (ls == null) return r; String l = escapeNCName(ls); if( l.length()==0 ) return null; return renameResource(r, l); } } /** * This pass translates annotations and stereotypes * to resources in the UML namespace and packages * to resources in the defaultNamespace. * * These are then available in later passes. */ Runnable pass1 = new Pass() { /** * Substitute a a single resource. * * @param r the resource to be (possibly) replaced. * @return the resource that should appear in result * or null if the resource should not appear in the result. */ @Override protected FrontsNode renameResource(OntResource r, String l) { if (r.hasProperty(RDF.type, UML.Stereotype)) return result.createResource(UML.NS + l.toLowerCase()); else if (r.hasProperty(RDF.type, OWL.AnnotationProperty)) { return annotationResource(l); } else if( r.hasProperty(RDF.type, UML.Package)) { // there are three strategies evolved over time to assign package URI's if( uniqueNamespaces ) { if( extraDecoration ) return result.createResource(stripHash(defaultNamespace) + "/Global" + pathName(r) + "#Package_" + l); else return result.createResource(stripHash(defaultNamespace) + prefixPath(r) + "#" + l); } else return result.createResource(defaultNamespace + "Package_" + l); } else return r; } }; public static String stripHash(String uri) { while( uri.endsWith("#") || uri.endsWith("/")) uri = uri.substring(0, uri.length()-1); return uri; } public static boolean powercc = true;; /** * Determine whether we are interested in a UML tag of given name * and return an annotation resource for it. */ public static FrontsNode annotationResource(String l) { if (l.equals("documentation") || l.equals("description")) return RDFS.comment; else if(l.equals(UML.baseuri.getLocalName())) return UML.baseuri; else if(l.equals(UML.baseprefix.getLocalName())) return UML.baseprefix; else if( powercc ) { if(l.equals("RationalRose$PowerCC:RdfRoleA")) return UML.roleALabel; else if(l.equals("RationalRose$PowerCC:RdfRoleB")) return UML.roleBLabel; else if(l.equals("RationalRose$PowerCC:Namespace")) return UML.baseprefix; } return null; // omit other annotations eg 'transient' defined in the UML } private static String pathName(OntResource r) { String prefix = prefixPath(r); String ls = r.getString(RDFS.label); if (ls == null) return prefix; String l = escapeNCName(ls); if( l.length()==0 ) return prefix; return prefix + "/" + l; } private static String prefixPath(OntResource r) { OntResource ps = r.getResource(RDFS.isDefinedBy); if( ps != null && ! ps.equals(UML.global_package)) { return pathName(ps); } else { return ""; } } /** * This pass translates anything found with a label and an XMI namespace, * which would exclude resources translated in earlier passes. * Special naming rules apply to classes, properties and enumeration members. */ Runnable pass2 = new Pass() { /** * Substitute a a single resource. * * @param r the resource to be (possibly) replaced. * @return the resource that should appear in result * or null if the resource should not appear in the result. */ @Override protected FrontsNode renameResource(OntResource r, String l) { String namespace = findBaseURI(r); if (r.hasProperty(RDF.type, OWL.Class)) { if ((r.hasProperty(UML.hasStereotype, UML.datatype) || r.hasProperty(UML.hasStereotype, UML.cimdatatype) || r.hasProperty(UML.hasStereotype, UML.primitive)) && ! r.hasProperty(UML.hasStereotype, UML.enumeration)) { FrontsNode x = selectXSDType(l); if (x != null) return x; } return result.createResource(namespace + l); } if (r.hasProperty(RDF.type, OWL.ObjectProperty) || r.hasProperty(RDF.type, OWL.DatatypeProperty) || r.hasProperty(RDF.type, RDF.Property)) { OntResource ds = r.getResource(RDFS.domain); if (ds != null) { String ls = ds.getString(RDFS.label); if (ls != null) { String c = escapeNCName(ls); return result.createResource(namespace + c + "." + l); } } return result.createResource(namespace + "_." + l); } for (ResIterator it = r.listProperties(RDF.type); it.hasNext();) { OntResource ts = it.nextResource(); if( ts.hasProperty(UML.hasStereotype, UML.enumeration)) { String ls = ts.getString(RDFS.label); if (ls != null) { String c = escapeNCName(ls); return result.createResource(namespace + c + "." + l); } } } //System.out.println("Unrecognised UML element name: " + l + " uri: " + r.getURI()); // this is almost certainly a top level datatype declaration FrontsNode x = selectXSDType(l); if( x != null) return x; return result.createResource(namespace + l); } }; /** * Regenerate a statement in the result model with given * subject, predicate and object. * * s, p, o are the resources from an input model statement * with possible substitutions. If any are null (no substitution) * the original statement is negated. */ protected void add(FrontsNode s, FrontsNode p, Node o) { if(s != null && p != null && o != null ) { result.add(s, p, o); } } /** * Select XSD datatypes for UML attributes. * * @param l A simple name for the datatype received from the UML. * @return A resource representing one of the XSD datatypes recommended for OWL. */ protected FrontsNode selectXSDType(String l) { // TODO: add more XSD datatypes here if( l.equalsIgnoreCase("integer")) return XSD.integer; else if( l.equalsIgnoreCase("int")) return XSD.xint; else if( l.equalsIgnoreCase("unsigned")) return XSD.unsignedInt; else if( l.equalsIgnoreCase("ulong") || l.equalsIgnoreCase("ulonglong")) return XSD.unsignedLong; else if( l.equalsIgnoreCase("short")) return XSD.xshort; else if( l.equalsIgnoreCase("long")|| l.equalsIgnoreCase("longlong")) return XSD.xlong; else if( l.equalsIgnoreCase("string") || l.equalsIgnoreCase("char")) return XSD.xstring; else if( l.equalsIgnoreCase("float")) return XSD.xfloat; else if( l.equalsIgnoreCase("double") || l.equalsIgnoreCase("longdouble")) return XSD.xdouble; else if( l.equalsIgnoreCase("boolean") || l.equalsIgnoreCase("bool")) return XSD.xboolean; else if( l.equalsIgnoreCase("decimal")) return XSD.decimal; else if( l.equalsIgnoreCase("nonNegativeInteger")) return XSD.nonNegativeInteger; else if( l.equalsIgnoreCase("date")) return XSD.date; else if( l.equalsIgnoreCase("time")) return XSD.time; else if( l.equalsIgnoreCase("datetime")) return XSD.dateTime; else if( l.equalsIgnoreCase("absolutedatetime")) return XSD.dateTime; else if( l.equalsIgnoreCase("duration")) return XSD.duration; else return null; } /** * Convert a string to an NCNAME by substituting underscores for * invalid characters. */ public static String escapeNCName(String label) { StringBuffer result = new StringBuffer(label.trim()); for(int ix = 0; ix < result.length(); ix++) { if( ! XMLChar.isNCName(result.charAt(ix))) result.setCharAt(ix, '_'); } if(result.length() > 0 && ! XMLChar.isNCNameStart(result.charAt(0))) result.insert(0, '_'); return result.toString(); } /** * Discover the base URI, if given, for a model element. * @param r an untranslated resource * @return a URI */ protected String findBaseURI(OntResource r) { String b = r.getString(UML.baseuri); if( b != null ) return b; String x = r.getString(UML.baseprefix); if( x != null ) { ResIterator it = model.listSubjectsWithProperty(UML.uriHasPrefix, x); if( it.hasNext() ) { b = it.nextResource().getURI(); if( ! b.contains("#")) b += "#"; return b; } } OntResource p = r.getResource(RDFS.isDefinedBy); if( p != null ) { b = p.getString(UML.baseuri); if( b != null ) return b; if( uniqueNamespaces ) if( extraDecoration ) return p.getNameSpace(); else return stripHash(p.getNameSpace()) + "/" + p.getLocalName() + "#"; } return defaultNamespace; } /** * Propagate a given annotation property to descendents. * */ private void propagateAnnotation(FrontsNode a) { ResIterator it = model.listSubjectsWithProperty(RDF.type, UML.Package); while( it.hasNext()) propagateAnnotation(it.nextResource(), a); } /** * Propagate a given annotation property from p's parent package to p. * */ private String propagateAnnotation(OntResource p, FrontsNode a) { String v = p.getString(a); if( v != null ) return v; OntResource s = p.getResource(RDFS.isDefinedBy); if( s != null ) { v = propagateAnnotation(s, a); if( v != null) { p.addProperty(a, v); return v; } } return null; } }