/*
* 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.profiles;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import au.com.langdale.kena.Composition;
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 au.com.langdale.kena.Resource;
import au.com.langdale.kena.ResourceFactory;
import au.com.langdale.profiles.ProfileClass.PropertyInfo;
import au.com.langdale.xmi.UML;
import com.hp.hpl.jena.graph.FrontsNode;
import com.hp.hpl.jena.vocabulary.OWL;
import com.hp.hpl.jena.vocabulary.XSD;
/**
* This class is the driver for a number of profile model transformation and
* conversion utilities. It traverses a profile model and fires events (defined
* by abstract methods) for each feature of the profile.
*
* TODO: this class has not been updated to handle OWL unions in the profile.
*/
public abstract class SchemaGenerator extends ProfileUtility implements Runnable {
protected OntModel model;
protected OntModel profileModel;
protected Catalog catalog;
protected Set datatypes = new HashSet();
protected Set packages = new HashSet();
protected PropertyAccumulator props = new PropertyAccumulator();
protected EnumAccumulator enums = new EnumAccumulator();
protected List work = new LinkedList(); // unprocessed profiles
private boolean withInverses;
private boolean preserveNamespaces;
private OntModel backgroundModel;
private String ontURI;
private OntResource ontNode;
public class Catalog extends BaseMap {
protected Map classes = new HashMap(); // base class to profile uri
private Map profiles = new HashMap(); // profile uri to base class
public Catalog() {
}
@Override
public void add(OntResource base, OntResource clss) {
if(clss.isAnon()) {
if( ! classes.containsKey(base)) {
add(base, constructURI(base));
}
}
else {
if( classes.containsKey(base) )
rename(base);
else
add( base, constructURI(base, clss));
}
super.add(base, clss); // purely to support findProfiles()
}
public void add(OntResource base, String uri) {
Object alias = profiles.get(uri);
if( alias != null ){
if(alias.equals(base))
return;
rename((OntResource)alias);
}
classes.put(base, uri);
profiles.put(uri, base);
}
public boolean add(OntResource base) {
if( classes.containsKey(base))
return false;
add(base, constructURI(base));
return true;
}
private void rename(OntResource base) {
String uri = constructURI(base);
String old = (String) classes.get(base);
if( old == null || uri.equals(old))
return;
classes.remove(base);
profiles.remove(old);
OntResource alias = (OntResource) profiles.get(uri);
if( alias != null )
rename(alias);
classes.put(base, uri);
profiles.put(uri, base);
}
public String getURI(OntResource base) {
return (String) classes.get(base);
}
public Collection getBases() {
return classes.keySet();
}
public Collection getURIs() {
return classes.values();
}
public OntModel buildLattice() {
OntModel hierarchy = ModelFactory.createTransInf();
Collection bases = getBases();
Iterator it = bases.iterator();
while(it.hasNext()) {
OntResource clss = (OntResource)it.next();
OntResource profile = hierarchy.createClass(getURI(clss));
Iterator jt = clss.listSuperClasses(false);
while(jt.hasNext()) {
OntResource superClass = (OntResource) jt.next();
if( bases.contains(superClass))
profile.addSuperClass(hierarchy.createClass(getURI(superClass)));
}
}
return hierarchy;
}
}
public static class TypeInfo {
public final String type, xsdtype;
public TypeInfo(OntResource range, SchemaGenerator context) {
if( range != null ) {
if( range.getNameSpace().equals(XSD.getURI())) {
type = null;
xsdtype = range.getURI();
}
else {
type = context.constructURI(range);
Resource cand = range.getEquivalentClass();
if( cand != null && cand.getNameSpace().equals(XSD.getURI()))
xsdtype = cand.getURI();
else
xsdtype = null;
}
}
else {
type = xsdtype = null;
}
}
}
public SchemaGenerator(OntModel profileModel, OntModel backgroundModel, boolean preserveNamespaces, boolean inverses) {
this.profileModel = profileModel;
this.backgroundModel = backgroundModel;
this.model = Composition.merge(profileModel, backgroundModel);
this.preserveNamespaces = preserveNamespaces;
this.catalog = new Catalog();
this.withInverses = inverses;
ontNode = profileModel.getValidOntology();
if(preserveNamespaces) {
OntResource base = backgroundModel.getValidOntology();
if( base != null )
ontURI = base.getURI();
else
ontURI = null;
}
else
ontURI = ontNode.getURI();
}
public SchemaGenerator(OntModel profileModel, OntModel backgroundModel) {
this( profileModel, backgroundModel, false, false);
}
public String getOntURI() {
return ontURI;
}
public void run() {
scanProfiles();
if(withInverses)
addInverseProperties();
scanDomainsAndRanges();
// emit classes first
Iterator it = catalog.getBases().iterator();
while( it.hasNext()) {
OntResource base = (OntResource)it.next();
generateClass(base);
}
// emit datatypes
Iterator nt = datatypes.iterator();
while( nt.hasNext()) {
OntResource type = (OntResource)nt.next();
TypeInfo info = new TypeInfo(type, this);
if( info.type != null) {
emitDatatype(info.type, info.xsdtype);
annotate(info.type, type);
}
}
// emit properties
Iterator lt = props.getGroups().iterator();
while( lt.hasNext()) {
generateProperty((PropertyGroup)lt.next());
}
// emit superclass relationships
generateLattice(catalog.buildLattice());
// emit any ontology (header) properties
generateOntologyProperties();
}
// construct a URI for a base model class, datatype, property or individual
private String constructURI(OntResource base) {
if(preserveNamespaces)
return base.getURI();
else
return ontURI + "#" + base.getLocalName();
}
// construct a URI for named profile
private String constructURI(OntResource base, OntResource profile) {
if( ! preserveNamespaces)
return profile.getURI(); // use the profile URI directly, usually in namespace but not always
else
return base.getURI();
}
protected void scanProfiles() {
Iterator it = ProfileClass.getProfileClasses(profileModel, model);
while( it.hasNext())
work.add(it.next());
while( ! work.isEmpty()) {
ProfileClass profile = (ProfileClass) work.remove(0);
scanProperties(profile);
OntResource base = profile.getBaseClass();
if( base == null) {
log("No base for profile class", profile.getSubject());
}
else {
catalog.add(base, profile.getSubject());
if((profile.isEnumerated() || profile.isRestrictedEnum()) && ! profile.isUnion())
enums.add(base, profile.getIndividuals());
}
}
}
protected boolean scanProperties(ProfileClass profile) {
Iterator it = profile.getProperties();
boolean some = it.hasNext();
while( it.hasNext()) {
PropertyInfo info = profile.getPropertyInfo((OntResource) it.next());
ProfileClass range_profile = props.add( info );
if( range_profile != null)
work.add(range_profile);
}
return some;
}
private void addInverseProperties() {
Iterator it = new ArrayList(props.getGroups()).iterator();
while( it.hasNext()) {
PropertyGroup group = (PropertyGroup) it.next();
PropertySpec info = group.getSummary();
OntResource inverse = info.prop.getInverse();
if( inverse != null && ! props.containsKey(inverse)) {
props.add(inverse, info.base_range, info.base_domain);
}
}
}
protected void scanDomainsAndRanges() {
Iterator it = props.getGroups().iterator();
while( it.hasNext()) {
PropertyGroup group = (PropertyGroup) it.next();
scanSpec(group.getSummary());
for (Iterator jt = group.getRestrictions().iterator(); jt.hasNext();) {
scanSpec((PropertySpec) jt.next());
}
}
}
protected void scanSpec(PropertySpec spec) {
catalog.add(spec.base_domain);
if( spec.base_range != null) {
catalog.add(spec.base_range);
}
else {
OntResource range = spec.prop.getRange();
if( range != null)
datatypes.add(range);
}
}
private void generateLattice(OntModel hierarchy) {
Collection uris = catalog.getURIs();
Iterator it = uris.iterator();
while(it.hasNext()) {
OntResource profile = hierarchy.createResource((String)it.next());
Iterator jt = profile.listSuperClasses(true);
while(jt.hasNext()) {
OntResource superClass = (OntResource) jt.next();
emitSuperClass(profile.getURI(), superClass.getURI());
}
}
}
private void generateClass(OntResource base) {
String uri = catalog.getURI(base);
emitClass(uri, base.getURI());
emitLabel(uri, ResourceFactory.createResource(uri).getLocalName());
emitComment(uri, extractComment(base), extractProfileComment(base));
generateIndividuals(uri, base);
generateBaseStereotypes(uri, base);
for (Iterator it = catalog.find(base).iterator(); it.hasNext();) {
OntResource clss = (OntResource) it.next();
generateStereotypes(uri, clss);
}
generatePackage(uri, base);
}
private void generateIndividuals(String type_uri, OntResource base) {
for (Iterator ix = enums.get(base).iterator(); ix.hasNext();) {
OntResource instance = (OntResource) ix.next();
String uri = constructURI(instance);
emitInstance(uri, instance.getURI(), type_uri);
annotate(uri, instance);
}
}
private void generateProperty(PropertyGroup group) {
PropertySpec info = group.getSummary();
OntResource prop = group.getProperty();
String uri = constructURI(prop);
String domain = catalog.getURI(info.base_domain);
if(prop.isDatatypeProperty()) {
TypeInfo range = new TypeInfo( prop.getRange(), this);
emitDatatypeProperty(uri, prop.getURI(), domain, range.type, range.xsdtype, info.required);
}
else {
String range = catalog.getURI(info.base_range);
emitObjectProperty(uri, prop.getURI(), domain, range, info.required, info.functional);
OntResource inverse = prop.getInverse();
if( inverse != null && props.containsKey(inverse)) {
emitInverse(uri, constructURI(inverse));
}
}
if( info.label != null)
emitLabel(uri, info.label);
generateRestrictions(group);
emitComment(uri, extractComment(prop), info.comment);
generateBaseStereotypes(uri, prop);
if(info.reference)
emitStereotype(uri, UML.byreference.getURI());
}
private void generateRestrictions(PropertyGroup group) {
String uri = constructURI(group.getProperty());
for (Iterator it = group.getRestrictions().iterator(); it.hasNext();) {
PropertySpec rest = (PropertySpec) it.next();
String domain = catalog.getURI(rest.base_domain);
if(rest.prop.isDatatypeProperty()) {
TypeInfo range = new TypeInfo( rest.prop.getRange(), this);
if( range.xsdtype != null)
emitRestriction(uri, domain, range.xsdtype);
}
else if( rest.base_range != null){
emitRestriction(uri, domain, catalog.getURI(rest.base_range));
}
if( rest.required || rest.functional ) {
emitRestriction(uri, domain, rest.required, rest.functional);
}
}
}
private void generateBaseStereotypes(String uri, OntResource base) {
ResIterator it = base.listProperties(UML.hasStereotype);
while (it.hasNext()) {
emitBaseStereotype(uri, it.nextResource().getURI());
}
}
private void generateOntologyProperties() {
if( ontURI != null ) {
emitHeader(ontURI, ontNode.getLabel(), ontNode.getComment());
for( ResIterator it = ontNode.listProperties(OWL.imports); it.hasNext();)
emitImport(it.nextResource().getURI());
for (ResIterator it = profileModel.listIndividuals(MESSAGE.Flag); it.hasNext();)
emitFlag(it.nextResource().getURI());
}
}
private void generateStereotypes(String uri, OntResource base) {
ResIterator it = base.listProperties(UML.hasStereotype);
while (it.hasNext()) {
emitStereotype(uri, it.nextResource().getURI());
}
}
private void generatePackage(String uri, OntResource base) {
Resource symbol = base.getIsDefinedBy();
if( symbol == null)
return;
OntResource pack = (OntResource)symbol;
String curi = constructURI(pack);
if( ! packages.contains(pack)) {
emitPackage(curi);
annotate(curi, pack);
packages.add(pack);
generatePackage(curi, pack);
}
emitDefinedBy(uri, curi);
}
private String extractProfileComment(OntResource base) {
String comment = null;
Iterator it = catalog.find(base).iterator();
while(it.hasNext())
comment = appendComment(comment, (OntResource) it.next());
return comment;
}
private void annotate(String uri, OntResource base) {
String label = base.getLabel(null);
if( label != null)
emitLabel(uri, label);
String comment = base.getComment(null);
if( comment != null)
emitComment(uri, comment, null);
}
protected abstract void emitLabel(String uri, String label);
protected abstract void emitComment(String uri, String baseComment, String profileComment);
protected abstract void emitSuperClass(String subClass, String superClass);
protected abstract void emitClass(String uri, String base) ;
protected abstract void emitInstance(String uri, String base, String type);
protected abstract void emitDatatype(String uri, String xsdtype) ;
protected abstract void emitDatatypeProperty(String uri, String base, String domain, String type, String xsdtype, boolean required) ;
protected abstract void emitObjectProperty(String uri, String base, String domain, String range, boolean required, boolean functional) ;
protected abstract void emitRestriction(String uri, String domain, String range);
protected abstract void emitRestriction(String uri, String domain, boolean required, boolean functional) ;
protected abstract void emitInverse(String uri, String iuri) ;
protected abstract void emitStereotype(String uri, String stereo) ;
protected abstract void emitBaseStereotype(String uri, String stereo) ;
protected abstract void emitHeader(String uri, String label, String comment);
protected abstract void emitFlag(String uri);
protected abstract void emitImport(String uri);
protected abstract void emitDefinedBy(String uri, String container);
protected abstract void emitPackage(String uri) ;
protected void log(String string, FrontsNode node) {
log(string + ": " + node);
}
protected void log(String item) {
System.out.println(item);
}
}