/*
* 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.jena;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import au.com.langdale.xmi.UML;
import com.hp.hpl.jena.graph.FrontsNode;
import au.com.langdale.inference.LOG;
import au.com.langdale.kena.OntModel;
import au.com.langdale.kena.ResIterator;
import au.com.langdale.kena.OntResource;
import au.com.langdale.kena.Resource;
import com.hp.hpl.jena.vocabulary.RDF;
import com.hp.hpl.jena.vocabulary.RDFS;
/**
* Represent an ontology as a tree using a UML inspired layout.
*
*/
public class UMLTreeModel extends JenaTreeModelBase {
/**
* The root node and nodes under packages will be
* packages or classes.
*/
@Override
protected Node classify(OntResource child) {
if( child.equals(UML.global_package))
return new GlobalNode();
if( child.hasProperty(RDF.type, UML.Package))
return new PackageNode(child);
else if( child.isClass()) {
if( child.hasProperty(UML.hasStereotype, UML.enumeration))
return new EnumClassNode(child);
else
return new ClassNode( child );
}
else if( child.isDatatypeProperty()) {
return new DatatypeNode(child); // used for the SubClassModel
}
return new Empty("");
}
/**
* Construct a list of resources representing a path starting
* from the root resource of this tree to the given target.
*/
@Override
protected List findResourcePathTo(FrontsNode symbol) {
OntResource target = asOntResource(symbol);
if( getRoot() == null || target == null)
return null;
OntResource start = getRoot().getSubject();
if( start == null)
return null;
ArrayList path = new ArrayList(6);
path.add(target);
while( ! target.equals(start)) {
if( target.isProperty()) {
target = target.getDomain();
if( target == null )
return null;
}
else {
ResIterator it = target.listRDFTypes(false);
OntResource enumerated = null;
while( it.hasNext()) {
OntResource cls = it.nextResource();
if( cls.hasProperty(UML.hasStereotype, UML.enumeration)) {
enumerated = cls;
break;
}
}
if( enumerated != null ) {
target = enumerated;
}
else {
OntResource pack = target.getIsDefinedBy();
if( pack == null)
target = getOntModel().createResource(UML.global_package.asNode());
else
target = pack;
}
}
if( path.contains(target))
return null;
path.add(target);
}
Collections.reverse(path);
return path;
}
/**
* The global package that includes anything without an explicit
* isDefinedBy property.
*/
public class GlobalNode extends ModelNode {
@Override
public boolean getErrorIndicator() {
return false;
}
@Override
public OntResource getSubject() {
return getOntModel().createResource(UML.global_package.asNode());
}
@Override
protected void populate() {
Set packages = getOntModel().listIndividuals(UML.Package).toSet();
packages.remove(UML.global_package);
addTopPackages(packages);
addTopClasses(packages);
addTopDatatypes(packages);
}
private void addTopPackages(Set packages) {
Iterator it = packages.iterator();
while( it.hasNext()) {
OntResource pack = (OntResource)it.next();
if( ! isDefinedBy(pack, packages))
add(new PackageNode(pack));
}
}
private void addTopClasses(Set packages) {
Iterator it = getOntModel().listNamedClasses();
while( it.hasNext()) {
OntResource clss = (OntResource)it.next();
if( ! isDefinedBy(clss, packages)) {
if( clss.hasProperty(UML.hasStereotype, UML.enumeration))
add(new EnumClassNode(clss));
else
add(new ClassNode(clss));
}
}
}
private void addTopDatatypes(Set packages) {
ResIterator it = getOntModel().listSubjectsWithProperty(RDF.type, RDFS.Datatype);
while( it.hasNext()) {
OntResource dt = it.nextResource();
if( ! isDefinedBy(dt, packages)) {
add(new DatatypeClassNode(dt));
}
}
}
}
private boolean isDefinedBy(OntResource subject, Set packages) {
boolean top = false;
Iterator jt = subject.listIsDefinedBy();
while( jt.hasNext()) {
if( packages.contains(jt.next())) {
top = true;
break;
}
}
return top;
}
/**
* A UML package.
*
*
*/
public class PackageNode extends ModelNode {
OntResource subject;
public PackageNode(OntResource pack) {
subject = pack;
}
@Override
protected void populate() {
OntModel m = subject.getOntModel();
ResIterator it = m.listSubjectsWithProperty(RDFS.isDefinedBy, subject) ;
while( it.hasNext()) {
OntResource child = it.nextResource();
if( child.hasRDFType(UML.Package))
add(new PackageNode(child));
else if( child.hasRDFType( RDFS.Datatype))
add(new DatatypeClassNode(child));
else if( child.isClass()){
if( child.hasProperty(UML.hasStereotype, UML.enumeration))
add(new EnumClassNode(child));
else if( child.hasProperty(UML.hasStereotype, UML.extension))
add(new ExtensionClassNode(child));
else if( child.hasProperty(UML.hasStereotype, UML.compound))
add(new CompoundClassNode(child));
else
add(new ClassNode( child ));
}
}
}
@Override
public String toString() {
return "Package: " + label(subject);
}
@Override
public OntResource getSubject() {
return subject;
}
@Override
protected String collation() {
return "1" + toString();
}
@Override
public boolean getErrorIndicator() {
return subject.hasProperty(LOG.hasProblems);
}
}
/**
* A class in the ontology.
*
*
*/
public class ClassBaseNode extends ModelNode {
OntResource subject;
public ClassBaseNode(OntResource clss) {
subject = clss;
}
@Override
protected void populate() {
// populate(true, false); // the old way
populate(false, true, true);
}
protected void populate(boolean inherited, boolean supers, boolean subs) {
// prevent tedious back links from child to parent
Node parent = getParent();
OntResource exclude = null;
if( parent != null) {
exclude = parent.getSubject();
if( exclude.isObjectProperty()) {
OntResource property = exclude;
exclude = property.getInverse();
if( exclude == null )
exclude = property.getInverseOf();
}
}
if( supers) {
ResIterator it = subject.listSuperClasses(false);
while( it.hasNext()) {
OntResource clss = it.nextResource();
if( ( exclude == null || ! clss.equals(exclude)) && clss.isURIResource() && clss.isClass()) {
if( clss.hasProperty(UML.hasStereotype, UML.extension))
add( new ExtensionNode( clss ));
else
add( new SuperClassNode( clss ));
}
}
}
if(subs) {
ResIterator it = subject.listSubClasses(false);
while( it.hasNext()) {
OntResource clss = it.nextResource();
if( ! clss.hasProperty(UML.hasStereotype, UML.enumeration) &&
( exclude == null || ! clss.equals(exclude)) &&
clss.isClass())
add( new SubClassNode(clss));
}
}
{
Iterator it = listProperties(inherited);
while( it.hasNext()) {
OntResource pt = (OntResource) it.next();
if( pt.isDatatypeProperty())
add( new DatatypeNode(pt));
else if( pt.getRange() != null && pt.getRange().hasProperty(UML.hasStereotype, UML.enumeration))
add( new EnumPropertyNode(pt));
else if(pt.hasProperty(UML.hasStereotype, UML.aggregateOf))
add( new AggregateNode(pt));
else if(pt.hasProperty(UML.hasStereotype, UML.compositeOf))
add( new CompositeNode(pt));
else if( pt.isFunctionalProperty())
add( new FunctionalNode(pt));
else if( pt.isInverseFunctionalProperty())
add( new InverseNode(pt));
else
add( new PropertyNode(pt));
}
}
{
ResIterator it = subject.listInstances();
while( it.hasNext())
add( new IndividualNode(it.nextResource()));
}
}
protected Iterator listProperties(boolean inherited) {
Set results = new HashSet();
addDirectProperties(subject, results);
if( inherited ) {
ResIterator it =subject.listSuperClasses(false);
while( it.hasNext()) {
addDirectProperties(it.nextResource(), results);
}
}
return results.iterator();
}
private void addDirectProperties(Resource subject, Set results) {
ResIterator it = getOntModel().listSubjectsWithProperty(RDFS.domain, subject);
while( it.hasNext()) {
OntResource pr = it.nextResource();
if( pr.isProperty())
results.add(pr);
}
}
@Override
public String toString() {
return "Class: " + label(subject);
}
@Override
protected String collation() {
return "2" + toString();
}
@Override
public OntResource getSubject() {
return subject;
}
@Override
public boolean getErrorIndicator() {
return subject.hasProperty(LOG.hasProblems);
}
}
public class ClassNode extends ClassBaseNode {
public ClassNode(OntResource clss) {
super(clss);
}
}
public class ExtensionClassNode extends ClassBaseNode {
public ExtensionClassNode(OntResource clss) {
super(clss);
}
}
public class CompoundClassNode extends ClassBaseNode {
public CompoundClassNode(OntResource clss) {
super(clss);
}
}
/**
* A class to be shown as a sub-class of the parent node.
*
*/
public class SubClassNode extends ClassBaseNode {
public SubClassNode(OntResource clss) {
super(clss);
}
@Override
protected void populate() {
populate(false, true, true);
}
@Override
public boolean isPruned() {
return true;
}
@Override
public String toString() {
return "SubClass: " + label(subject);
}
@Override
protected String collation() {
return "7" + toString();
}
}
/**
* A class to be shown as a super-class of the parent node.
*
*/
public class SuperClassNode extends ClassBaseNode {
public SuperClassNode(OntResource clss) {
super(clss);
}
@Override
protected void populate() {
populate(false, true, false);
}
@Override
public boolean isPruned() {
return true;
}
@Override
public String toString() {
return "SuperClass: " + label(subject);
}
@Override
protected String collation() {
return "6" + toString();
}
}
/**
* A class to be shown as an extension of the parent node.
*
*/
public class ExtensionNode extends ClassBaseNode {
public ExtensionNode(OntResource clss) {
super(clss);
}
@Override
protected void populate() {
populate(false, true, false);
}
@Override
public boolean isPruned() {
return true;
}
@Override
public String toString() {
return "Extension: " + label(subject);
}
@Override
protected String collation() {
return "5" + toString();
}
}
/**
* An enumerated class.
*
*/
public class EnumClassNode extends ClassBaseNode {
public EnumClassNode(OntResource clss) {
super(clss);
}
@Override
protected void populate() {
populate(false, true, true);
}
}
/**
* A datatype class node (appearing below package)
*/
public class DatatypeClassNode extends ModelNode {
OntResource subject;
public DatatypeClassNode(OntResource subject) {
this.subject = subject;
}
@Override
public OntResource getSubject() {
return subject;
}
@Override
protected void populate() {
// no children
}
@Override
public String toString() {
OntResource base = subject.getEquivalentClass();
String units = subject.getString(UML.hasUnits);
return "Datatype: " + label(subject)
+ (base != null? " = " + label(base): "")
+ (units != null? " (" + units + ")": "");
}
@Override
public Class getIconClass() {
return DatatypeNode.class;
}
@Override
public boolean getErrorIndicator() {
return subject.hasProperty(LOG.hasProblems);
}
@Override
public boolean getAllowsChildren() {
return false;
}
}
/**
* A datatype property in the ontology.
*
*/
public class DatatypeNode extends ModelNode {
OntResource subject;
public DatatypeNode( OntResource prop) {
subject = prop;
}
@Override
public boolean getAllowsChildren() {
return false;
}
@Override
protected void populate() {
// no children
}
@Override
public String toString() {
OntResource range = subject.getRange();
String units = range != null? range.getString(UML.hasUnits): null;
String value = subject.getString(UML.hasInitialValue);
String rname = label(range);
return label(subject)
+ (range != null && !rname.equals("<undefined>")? ": " + rname: "")
+ (value != null? " = " + value: "")
+ (units != null? " (" + units + ")": "");
}
@Override
protected String collation() {
return "3" + toString();
}
@Override
public OntResource getSubject() {
return subject;
}
@Override
public boolean getErrorIndicator() {
return subject.hasProperty(LOG.hasProblems);
}
}
/**
* A general object property.
*
*/
public class PropertyNode extends ModelNode {
OntResource subject;
public PropertyNode( OntResource prop) {
subject = prop;
}
@Override
protected void populate() {
OntResource range = subject.getRange();
if( range != null && range.isClass())
adopt(new ClassNode(range));
}
@Override
public String toString() {
return prop_label(subject);
}
@Override
protected String collation() {
return "4" + toString();
}
@Override
public OntResource getSubject() {
return subject;
}
@Override
public boolean getErrorIndicator() {
return subject.hasProperty(LOG.hasProblems);
}
@Override
public boolean isPruned() {
return true;
}
}
/**
* A aggregate object property.
*
*/
public class AggregateNode extends PropertyNode {
public AggregateNode(OntResource prop) {
super(prop);
}
}
/**
* A composite object property.
*
*/
public class CompositeNode extends PropertyNode {
public CompositeNode(OntResource prop) {
super(prop);
}
}
/**
* A functional object property.
*
*/
public class FunctionalNode extends PropertyNode {
public FunctionalNode(OntResource prop) {
super(prop);
}
}
/**
* An inverse functional object property.
*
*/
public class InverseNode extends PropertyNode {
public InverseNode(OntResource prop) {
super(prop);
}
}
/**
* A functional property whose object is an enumeration.
*
*
*/
public class EnumPropertyNode extends PropertyNode {
public EnumPropertyNode(OntResource prop) {
super(prop);
}
@Override
protected void populate() {
OntResource range = subject.getRange();
adopt(new EnumClassNode(range));
}
@Override
protected String collation() {
return "3" + toString();
}
}
/**
* An instance (individual) of an enumerated class.
*
*
*/
public class IndividualNode extends ModelNode {
OntResource subject;
public IndividualNode( OntResource res) {
subject = res;
}
@Override
protected void populate() {
// no children
}
@Override
public boolean getAllowsChildren() {
return false;
}
@Override
public OntResource getSubject() {
return subject;
}
@Override
public boolean getErrorIndicator() {
return subject.hasProperty(LOG.hasProblems);
}
}
}