package net.enilink.komma.parser.manchester; import java.util.Collection; import java.util.List; import net.enilink.komma.core.URI; import net.enilink.komma.core.URIs; import net.enilink.komma.parser.BaseRdfParser; import net.enilink.komma.parser.manchester.tree.Annotation; import net.enilink.komma.parser.sparql.tree.BNode; import net.enilink.komma.parser.sparql.tree.BooleanLiteral; import net.enilink.komma.parser.sparql.tree.GraphNode; import net.enilink.komma.parser.sparql.tree.IriRef; import net.enilink.komma.parser.sparql.tree.Literal; import net.enilink.komma.parser.sparql.tree.QName; import net.enilink.vocab.owl.OWL; import net.enilink.vocab.rdf.RDF; import net.enilink.vocab.rdfs.RDFS; import net.enilink.vocab.xmlschema.XMLSCHEMA; import org.parboiled.Rule; import org.parboiled.support.Var; /** * Parser for Manchester OWL Syntax * * @see <a href="http://www.w3.org/TR/owl2-manchester-syntax/">Manchester OWL * Syntax</a> * * @author Ken Wenzel */ public class ManchesterSyntaxParser extends BaseRdfParser { public IManchesterActions actions; public ManchesterSyntaxParser() { this(new IManchesterActions() { @Override public boolean createStmt(Object subject, Object predicate, Object object) { return true; } }); } public ManchesterSyntaxParser(IManchesterActions actions) { this.actions = actions; } // 2.1 IRIs, Integers, Literals, and Entities public Rule AnnotatedList(Var<? extends GraphNode> source, URI property, Rule target) { return sequence(WithAnnotations(source, property, target), zeroOrMore(',', WithAnnotations(source, property, target))); } public Rule Annotation() { return sequence(IriRef(), AnnotationTarget(), // push(new Annotation((GraphNode) pop(1), (GraphNode) pop()))); } public Rule AnnotationPropertyFrame() { Var<GraphNode> annotationPropertyIri = new Var<>(); return sequence( "AnnotationProperty:", IriRef(), annotationPropertyIri.set((GraphNode) pop()), actions.createStmt(annotationPropertyIri, RDF.PROPERTY_TYPE, OWL.TYPE_ANNOTATIONPROPERTY), zeroOrMore(firstOf( sequence( "Annotations:", AnnotatedList(annotationPropertyIri, null, Annotation())), sequence( "Domain:", AnnotatedList(annotationPropertyIri, RDFS.PROPERTY_DOMAIN, IriRef())), sequence( "Range:", AnnotatedList(annotationPropertyIri, RDFS.PROPERTY_RANGE, IriRef())), sequence( "SubPropertyOf:", AnnotatedList(annotationPropertyIri, RDFS.PROPERTY_SUBPROPERTYOF, IriRef()))))); } @SuppressWarnings("unchecked") public Rule WithAnnotations(Var<? extends GraphNode> source, URI property, Rule target) { return sequence( Annotations(), target, createAnnotatedStmt((List<Annotation>) pop(1), source.get(), property, pop())); } public boolean createAnnotatedStmt(List<Annotation> annotations, Object source, URI property, Object target) { if (source != null && property != null) { // this is the annotated statement actions.createStmt(source, property, target); } else if (target instanceof Annotation) { // this is an annotated annotation push(target); source = target; property = URIs.createURI(((Annotation) target).getPredicate() .toString()); target = ((Annotation) target).getObject(); } for (Annotation annotation : annotations) { if (property == null) { actions.createStmt(source, annotation.getPredicate(), annotation.getObject()); } else { BNode annotationNode = new BNode(); actions.createStmt(annotationNode, RDF.PROPERTY_TYPE, OWL.TYPE_ANNOTATIONPROPERTY); actions.createStmt(annotationNode, OWL.PROPERTY_ANNOTATEDSOURCE, source); actions.createStmt(annotationNode, OWL.PROPERTY_ANNOTATEDPROPERTY, property); actions.createStmt(annotationNode, OWL.PROPERTY_ANNOTATEDTARGET, target); actions.createStmt(annotationNode, annotation.getPredicate(), annotation.getObject()); } } return true; } public Rule Annotations() { return sequence( push(LIST_BEGIN), zeroOrMore( "Annotations:", AnnotatedList(new Var<>((GraphNode) null), null, Annotation())), push(popList(Annotation.class))); } public Rule AnnotationTarget() { return firstOf(BlankNode(), IriRef(), Literal()); } // 2.2 Ontologies and Annotations public Rule Atomic() { return firstOf(IriRef(), sequence('{', List(Individual()), '}'), sequence('(', Description(), ')')); } public Rule ClassFrame() { Var<GraphNode> classIri = new Var<>(); return sequence( "Class:", IriRef(), classIri.set((GraphNode) pop()), actions.createStmt(classIri, RDF.PROPERTY_TYPE, OWL.TYPE_CLASS), zeroOrMore(firstOf( sequence("Annotations:", AnnotatedList(classIri, null, Annotation())), sequence( "SubClassOf:", AnnotatedList(classIri, RDFS.PROPERTY_SUBCLASSOF, Description())), sequence( "EquivalentTo:", AnnotatedList(classIri, OWL.PROPERTY_EQUIVALENTCLASS, Description())), sequence( "DisjointWith:", AnnotatedList(classIri, OWL.PROPERTY_DISJOINTWITH, Description())), sequence( "DisjointUnionOf:", WithAnnotations( classIri, OWL.PROPERTY_DISJOINTUNIONOF, sequence( List2(Description()), push(createRdfList((List<?>) pop()))))), sequence( "HasKey:", WithAnnotations(classIri, OWL.PROPERTY_HASKEY, oneOrMore(PropertyExpression())))))); } public Rule PropertyExpression() { return firstOf(ObjectPropertyExpression(), DataPropertyExpression()); } public Rule Conjunction() { return firstOf( sequence(IriRef(), "that", optional("not"), Restriction(), zeroOrMore("and", optional("not"), Restriction())), sequence( Primary(), optional(push(LIST_BEGIN), oneOrMore("and", Primary()), push(new BNode()), // actions.createStmt(peek(), RDF.PROPERTY_TYPE, OWL.TYPE_CLASS), // actions.createStmt( peek(), OWL.PROPERTY_INTERSECTIONOF, createRdfList(popList(1, GraphNode.class, 1)))))); } public GraphNode createRdfList(Collection<? extends Object> elements) { GraphNode head = null, last = null; for (Object element : elements) { GraphNode current = new BNode(); actions.createStmt(current, RDF.PROPERTY_TYPE, RDF.TYPE_LIST); actions.createStmt(current, RDF.PROPERTY_FIRST, element); if (last != null) { actions.createStmt(last, RDF.PROPERTY_REST, current); } if (head == null) { head = current; } last = current; } actions.createStmt(last, RDF.PROPERTY_REST, RDF.NIL); return head; } public Rule DataAtomic() { return firstOf(DatatypeRestriction(), sequence('{', List(Literal()), '}'), sequence('(', DataRange(), ')')); } public Rule DataConjunction() { return sequence( DataPrimary(), optional(push(LIST_BEGIN), oneOrMore("and", DataPrimary()), push(new BNode()), actions.createStmt(peek(), OWL.PROPERTY_INTERSECTIONOF, createRdfList(popList(1, GraphNode.class, 1))))); } public Rule DataPrimary() { return sequence(optional("not"), DataAtomic()); } public Rule DataPropertyExpression() { return IriRef(); } public Rule DataPropertyFact() { return sequence(IriRef(), Literal()); } public Rule DataPropertyFrame() { Var<GraphNode> datapropertyIri = new Var<>(); return sequence( "DataProperty:", IriRef(), datapropertyIri.set((GraphNode) pop()), actions.createStmt(datapropertyIri, RDF.PROPERTY_TYPE, OWL.TYPE_DATATYPEPROPERTY), zeroOrMore(firstOf( sequence( "Annotations:", AnnotatedList(datapropertyIri, null, Annotation())), sequence( "Domain:", AnnotatedList(datapropertyIri, RDFS.PROPERTY_DOMAIN, Description())), sequence( "Range:", AnnotatedList(datapropertyIri, RDFS.PROPERTY_RANGE, DataRange())), // TODO Annotations sequence( "Characteristics:", WithAnnotations( datapropertyIri, RDF.PROPERTY_TYPE, sequence( "Functional", push(new IriRef( OWL.TYPE_FUNCTIONALPROPERTY .toString()))))), sequence( "SubPropertyOf:", AnnotatedList(datapropertyIri, RDFS.PROPERTY_SUBPROPERTYOF, DataPropertyExpression())), sequence( "EquivalentTo:", AnnotatedList(datapropertyIri, OWL.PROPERTY_EQUIVALENTPROPERTY, DataPropertyExpression())), sequence( "DisjointWith:", AnnotatedList(datapropertyIri, OWL.PROPERTY_DISJOINTWITH, DataPropertyExpression()))))); } public Rule DataRange() { return sequence( DataConjunction(), optional(push(LIST_BEGIN), oneOrMore("or", DataConjunction()), push(new BNode()), actions.createStmt(peek(), OWL.PROPERTY_UNIONOF, createRdfList(popList(1, GraphNode.class, 1))))); } // 2.3 Property and Datatype Expressions public Rule Datatype() { return firstOf( IriRef(), sequence(firstOf("integer", "decimal", "float", "string"), push(new IriRef(XMLSCHEMA.NAMESPACE + match())))); } public Rule DatatypeFrame() { Var<GraphNode> datatypeIri = new Var<>(); return sequence( "Datatype:", Datatype(), datatypeIri.set((GraphNode) pop()), actions.createStmt(datatypeIri, RDF.PROPERTY_TYPE, RDFS.TYPE_DATATYPE), zeroOrMore("Annotations:", AnnotatedList(datatypeIri, null, Annotation())), // TODO Annotations, dataRange optional( "EquivalentTo:", WithAnnotations(datatypeIri, OWL.PROPERTY_EQUIVALENTCLASS, DataRange())), zeroOrMore("Annotations:", AnnotatedList(datatypeIri, null, Annotation()))); } public Rule DatatypeRestriction() { Var<BNode> restriction = new Var<>(); return sequence( Datatype(), optional( '[', restriction.set(new BNode()), actions.createStmt(restriction.get(), RDF.PROPERTY_TYPE, RDFS.TYPE_DATATYPE), actions.createStmt(restriction.get(), OWL.PROPERTY_ONDATATYPE, pop()), push(LIST_BEGIN), // push(new BNode()), // Facet(), RestrictionValue(), actions.createStmt( peek(2), pop(1), pop()), zeroOrMore(',', push(new BNode()), // Facet(), RestrictionValue(), // actions.createStmt(peek(2), pop(1), pop())), ']', actions.createStmt(restriction.get(), OWL.PROPERTY_WITHRESTRICTIONS, createRdfList(popList(GraphNode.class))), push(restriction.get()))); }; public Rule Description() { return sequence( Conjunction(), optional(push(LIST_BEGIN), oneOrMore("or", Conjunction()), push(new BNode()), // actions.createStmt(peek(), RDF.PROPERTY_TYPE, OWL.TYPE_CLASS), // actions.createStmt(peek(), OWL.PROPERTY_UNIONOF, createRdfList(popList(1, GraphNode.class, 1))))); } public Rule Entity() { return firstOf(sequence("Datatype", '(', Datatype(), ')'), // sequence("Class", '(', IriRef(), ')'), // sequence("ObjectProperty", '(', IriRef(), ')'), // sequence("DataProperty", '(', IriRef(), ')'), // sequence("AnnotationProperty", '(', IriRef(), // ')'), // sequence("NamedIndividual", '(', IriRef(), ')')); } public Rule Facet() { return sequence( firstOf("length", "minLength", "maxLength", "pattern", "langPattern", "<=", '<', ">=", '>'), push(createFacet(match().trim()))); } public IriRef createFacet(String facet) { String name = facet; if ("<=".equals(facet)) { name = "minInclusive"; } else if ("<".equals(facet)) { name = "minExclusive"; } else if (">=".equals(facet)) { name = "maxInclusive"; } else if (">".equals(facet)) { name = "maxExclusive"; } return new IriRef(XMLSCHEMA.NAMESPACE + name); } public Rule Fact(Var<GraphNode> individualIri) { Var<Boolean> not = new Var<>(false); return sequence(optional("not", not.set(true)), firstOf(ObjectPropertyFact(), DataPropertyFact()), createPropertyAssertion(individualIri.get(), not.get())); } public boolean createPropertyAssertion(GraphNode individual, boolean negative) { if (negative) { BNode assertion = new BNode(); GraphNode property = (GraphNode) pop(1); Object value = pop(); actions.createStmt(assertion, RDF.PROPERTY_TYPE, OWL.TYPE_NEGATIVEPROPERTYASSERTION); actions.createStmt(assertion, OWL.PROPERTY_SOURCEINDIVIDUAL, individual); actions.createStmt(assertion, OWL.PROPERTY_ASSERTIONPROPERTY, property); actions.createStmt(assertion, value instanceof Literal ? OWL.PROPERTY_TARGETVALUE : OWL.PROPERTY_TARGETINDIVIDUAL, value); push(assertion); } else { actions.createStmt(pop(1), pop(), individual); push(individual); } return true; } public Rule Frame() { return firstOf(DatatypeFrame(), // ClassFrame(), // ObjectPropertyFrame(), // DataPropertyFrame(), // AnnotationPropertyFrame(), // IndividualFrame(), // Misc() // ); } public Rule ImportOntology() { return sequence("Import:", IRI_REF_WS()); } public Rule Individual() { return firstOf(IriRef(), BlankNode()); } public Rule IndividualFrame() { Var<GraphNode> individualIri = new Var<>(); return sequence( "Individual:", Individual(), individualIri.set((GraphNode) pop()), actions.createStmt(individualIri.get(), RDF.PROPERTY_TYPE, OWL.TYPE_INDIVIDUAL), zeroOrMore(firstOf( sequence( "Annotations:", AnnotatedList(individualIri, null, Annotation())), sequence( "Types:", AnnotatedList(individualIri, RDF.PROPERTY_TYPE, Description())), sequence( "Facts:", AnnotatedList(individualIri, null, Fact(individualIri))), sequence( "SameAs:", AnnotatedList(individualIri, OWL.PROPERTY_SAMEAS, Individual())), sequence( "DifferentFrom:", AnnotatedList(individualIri, OWL.PROPERTY_DIFFERENTFROM, Individual()))))); } public Rule InverseObjectProperty() { return sequence("inverse", IriRef()); } // 2.4 Descriptions public Rule IRI_REF_WS() { return sequence(IRI_REF(), WS()); } public Rule IriRef() { return firstOf(IRI_REF(), PrefixedName(), sequence(PN_LOCAL(), push(new QName("", (String) pop())))); } public Rule List(Rule element) { return sequence(push(LIST_BEGIN), element, zeroOrMore(',', element), push(createRdfList(popList(GraphNode.class)))); } public Rule List2(Rule element) { return sequence(push(LIST_BEGIN), element, oneOrMore(sequence(',', element)), push(popList(GraphNode.class))); } // 2.5 Frames and Miscellaneous public Rule Literal() { return firstOf(RdfLiteral(), NumericLiteral(), BooleanLiteral()); } public Rule Misc() { return firstOf( sequence("EquivalentClasses:", Annotations(), List2(Description())), // sequence("DisjointClasses:", Annotations(), List2(Description())), // sequence("EquivalentProperties:", Annotations(), List2(IriRef())), // sequence("DisjointProperties:", Annotations(), List2(IriRef())), // sequence("SameIndividual:", Annotations(), List2(Individual())), // sequence("DifferentIndividuals:", Annotations(), List2(Individual()))); } public Rule ObjectPropertyCharacteristic() { return sequence( firstOf("Functional", "InverseFunctional", "Reflexive", "Irreflexive", "Symmetric", "Asymmetric", "Transitive"), push(new IriRef(OWL.NAMESPACE_URI.appendFragment( match().trim() + "Property").toString()))); } public Rule ObjectPropertyExpression() { return firstOf(IriRef(), InverseObjectProperty()); } public Rule ObjectPropertyFact() { return sequence(IriRef(), Individual()); } public Rule ObjectPropertyFrame() { Var<GraphNode> objectPropertyIri = new Var<>(); return sequence( "ObjectProperty:", IriRef(), objectPropertyIri.set((GraphNode) pop()), actions.createStmt(objectPropertyIri, RDF.PROPERTY_TYPE, OWL.TYPE_OBJECTPROPERTY), zeroOrMore(firstOf( sequence( "Annotations:", AnnotatedList(objectPropertyIri, null, Annotation())), sequence( "Domain:", AnnotatedList(objectPropertyIri, RDFS.PROPERTY_DOMAIN, Description())), sequence( "Range:", AnnotatedList(objectPropertyIri, RDFS.PROPERTY_RANGE, Description())), sequence( "Characteristics:", AnnotatedList(objectPropertyIri, RDF.PROPERTY_TYPE, ObjectPropertyCharacteristic())), sequence( "SubPropertyOf:", AnnotatedList(objectPropertyIri, RDFS.PROPERTY_SUBPROPERTYOF, ObjectPropertyExpression())), sequence( "EquivalentTo:", AnnotatedList(objectPropertyIri, OWL.PROPERTY_EQUIVALENTPROPERTY, ObjectPropertyExpression())), sequence( "DisjointWith:", AnnotatedList(objectPropertyIri, OWL.PROPERTY_DISJOINTWITH, ObjectPropertyExpression())), sequence( "InverseOf:", AnnotatedList(objectPropertyIri, OWL.PROPERTY_INVERSEOF, ObjectPropertyExpression())), // TODO sequence( "SubPropertyChain:", WithAnnotations( objectPropertyIri, OWL.PROPERTY_PROPERTYCHAINAXIOM, sequence( push(LIST_BEGIN), oneOrMore(sequence( ObjectPropertyExpression(), 'o', ObjectPropertyExpression())), push(createRdfList(popList(GraphNode.class)))))) // ))); } public Rule Ontology() { Var<IriRef> ontologyIri = new Var<>(); return sequence( "Ontology:", optional(sequence( OntologyIRI(), ontologyIri.set((IriRef) pop()), actions.createStmt(ontologyIri.get(), RDF.PROPERTY_TYPE, OWL.TYPE_ONTOLOGY), optional(sequence(VersionIRI(), actions.createStmt( ontologyIri.get(), OWL.PROPERTY_VERSIONINFO, match()))))), // zeroOrMore(ImportOntology()), // // FIXME What should be done if ontologyIri is null? Annotations(), // zeroOrMore(Frame())); } public Rule OntologyDocument() { return sequence(zeroOrMore(PrefixDeclaration()), Ontology(), EOI); } public Rule OntologyIRI() { return IRI_REF_WS(); } public Rule PrefixDeclaration() { return sequence("Prefix:", PNAME_NS(), IRI_REF()); } public Rule Primary() { Var<Boolean> isComplement = new Var<>(false); return sequence( optional("not", isComplement.set(true)), firstOf(Restriction(), Atomic()), // create complement class firstOf(isComplement.get() // && push(new BNode()) // && actions.createStmt(peek(), RDF.PROPERTY_TYPE, OWL.TYPE_CLASS) // && actions.createStmt(peek(), OWL.PROPERTY_COMPLEMENTOF, pop(1)), true)); } public Rule Restriction() { Var<URI> type = new Var<>(); Var<URI> on = new Var<>(); return sequence( firstOf(sequence( DataPropertyExpression(), firstOf(sequence("some", type.set(OWL.PROPERTY_SOMEVALUESFROM), DataPrimary()), // sequence("only", type.set(OWL.PROPERTY_ALLVALUESFROM), DataPrimary()), sequence("value", type.set(OWL.PROPERTY_HASVALUE), Literal()), // sequence( "min", type.set(OWL.PROPERTY_MINCARDINALITY), INTEGER(), optional(DataPrimary(), on .set(OWL.PROPERTY_ONDATARANGE))), // sequence( "max", type.set(OWL.PROPERTY_MAXCARDINALITY), INTEGER(), optional(DataPrimary(), on .set(OWL.PROPERTY_ONDATARANGE))), // sequence( "exactly", type.set(OWL.PROPERTY_CARDINALITY), INTEGER(), optional(DataPrimary(), on .set(OWL.PROPERTY_ONDATARANGE))))), sequence( ObjectPropertyExpression(), firstOf(sequence("some", type.set(OWL.PROPERTY_SOMEVALUESFROM), Primary()), sequence( "only", type.set(OWL.PROPERTY_ALLVALUESFROM), Primary()), sequence( "value", type.set(OWL.PROPERTY_HASVALUE), Individual()), sequence("Self", type.set(OWL.PROPERTY_HASSELF), push(new BooleanLiteral(true))), // sequence( "min", type.set(OWL.PROPERTY_MINCARDINALITY), INTEGER(), optional( Primary(), on.set(OWL.PROPERTY_ONCLASS))), // sequence( "max", type.set(OWL.PROPERTY_MAXCARDINALITY), INTEGER(), optional( Primary(), on.set(OWL.PROPERTY_ONCLASS))), // sequence( "exactly", type.set(OWL.PROPERTY_CARDINALITY), INTEGER(), optional( Primary(), on.set(OWL.PROPERTY_ONCLASS))))) // ), createRestriction(type.get(), on.get())); } public boolean createRestriction(URI type, URI on) { Object onTarget = null; if (on != null) { onTarget = pop(); if (type.equals(OWL.PROPERTY_CARDINALITY)) { type = OWL.PROPERTY_QUALIFIEDCARDINALITY; } else if (type.equals(OWL.PROPERTY_MAXCARDINALITY)) { type = OWL.PROPERTY_MAXQUALIFIEDCARDINALITY; } else if (type.equals(OWL.PROPERTY_MINCARDINALITY)) { type = OWL.PROPERTY_MINQUALIFIEDCARDINALITY; } } GraphNode property = (GraphNode) pop(1); BNode restriction = new BNode(); actions.createStmt(restriction, RDF.PROPERTY_TYPE, OWL.TYPE_RESTRICTION); actions.createStmt(restriction, OWL.PROPERTY_ONPROPERTY, property); actions.createStmt(restriction, type, pop()); if (on != null) { actions.createStmt(restriction, on, onTarget); } push(restriction); return true; } public Rule RestrictionValue() { return Literal(); } public Rule VersionIRI() { return IRI_REF_WS(); } }