/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Package
///////////////
package org.apache.jena.ontology.impl;
// Imports
///////////////
import java.util.*;
import java.util.function.Function;
import java.util.function.Predicate;
import org.apache.jena.datatypes.xsd.XSDDatatype ;
import org.apache.jena.enhanced.* ;
import org.apache.jena.graph.* ;
import org.apache.jena.ontology.* ;
import org.apache.jena.rdf.model.* ;
import org.apache.jena.rdf.model.impl.* ;
import org.apache.jena.reasoner.* ;
import org.apache.jena.shared.* ;
import org.apache.jena.util.ResourceUtils ;
import org.apache.jena.util.iterator.* ;
import org.apache.jena.vocabulary.* ;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* <p>
* Abstract base class to provide shared implementation for implementations of ontology
* resources.
* </p>
*/
public class OntResourceImpl
extends ResourceImpl
implements OntResource
{
// Constants
//////////////////////////////////
/** List of namespaces that are reserved for known ontology languages */
public static final String[] KNOWN_LANGUAGES = new String[] {OWL.NS,
RDF.getURI(),
RDFS.getURI(),
XSDDatatype.XSD};
// Static variables
//////////////////////////////////
/**
* A factory for generating OntResource facets from nodes in enhanced graphs.
* Note: should not be invoked directly by user code: use
* {@link org.apache.jena.rdf.model.RDFNode#as as()} instead.
*/
@SuppressWarnings("hiding")
public static Implementation factory = new Implementation() {
@Override
public EnhNode wrap( Node n, EnhGraph eg ) {
if (canWrap( n, eg )) {
return new OntResourceImpl( n, eg );
}
else {
throw new ConversionException( "Cannot convert node " + n.toString() + " to OntResource");
}
}
@Override
public boolean canWrap( Node node, EnhGraph eg ) {
// node will support being an OntResource facet if it is a uri or bnode
return node.isURI() || node.isBlank();
}
};
private static final Logger log = LoggerFactory.getLogger( OntResourceImpl.class );
// Instance variables
//////////////////////////////////
// Constructors
//////////////////////////////////
/**
* <p>
* Construct an ontology resource represented by the given node in the given graph.
* </p>
*
* @param n The node that represents the resource
* @param g The enh graph that contains n
*/
public OntResourceImpl( Node n, EnhGraph g ) {
super( n, g );
}
// External signature methods
//////////////////////////////////
/**
* <p>Answer the model that this resource is attached to, assuming that it
* is an {@link OntModel}. If this resource is not attached to any model,
* or is (unusually) attached to a model that is not an <code>OntModel</code>,
* answer null.</p>
* @return The ont model that this resource is attached to, or null.
*/
@Override
public OntModel getOntModel() {
Model m = getModel();
return (m instanceof OntModel) ? (OntModel) m : null;
}
/**
* <p>
* Answer the ontology language profile that governs the ontology model to which
* this ontology resource is attached.
* </p>
*
* @return The language profile for this ontology resource
* @throws JenaException if the resource is not bound to an OntModel, since
* that's the only way to get the profile for the resource
*/
@Override
public Profile getProfile() {
try {
return ((OntModel) getModel()).getProfile();
}
catch (ClassCastException e) {
throw new JenaException( "Resource " + toString() + " is not attached to an OntModel, so cannot access its language profile" );
}
}
/**
* <p>Answer true if this resource is a symbol in one of the standard ontology
* languages supported by Jena: RDF, RDFS, OWL or DAML+OIL. Since these languages
* have restricted namespaces, this check is simply a convenient way of testing whether
* this resource is in one of those pre-declared namespaces.</p>
* @return True if this is a term in the language namespace for OWL, RDF, RDFS or DAML+OIL.
*/
@Override
public boolean isOntLanguageTerm() {
if (!isAnon()) {
for ( String KNOWN_LANGUAGE : KNOWN_LANGUAGES )
{
if ( getURI().startsWith( KNOWN_LANGUAGE ) )
{
return true;
}
}
}
return false;
}
// sameAs
/**
* <p>Assert equivalence between the given resource and this resource. Any existing
* statements for <code>sameAs</code> will be removed.</p>
* @param res The resource that is declared to be the same as this resource
* @exception ProfileException If the {@link Profile#SAME_AS()} property is not supported in the current language profile.
*/
@Override
public void setSameAs( Resource res ) {
setPropertyValue( getProfile().SAME_AS(), "SAME_AS", res );
}
/**
* <p>Add a resource that is declared to be equivalent to this resource.</p>
* @param res A resource that declared to be the same as this resource
* @exception ProfileException If the {@link Profile#SAME_AS()} property is not supported in the current language profile.
*/
@Override
public void addSameAs( Resource res ) {
addPropertyValue( getProfile().SAME_AS(), "SAME_AS", res );
}
/**
* <p>Answer a resource that is declared to be the same as this resource. If there is
* more than one such resource, an arbitrary selection is made.</p>
* @return res An ont resource that declared to be the same as this resource
* @exception ProfileException If the {@link Profile#SAME_AS()} property is not supported in the current language profile.
*/
@Override
public OntResource getSameAs() {
return objectAsResource( getProfile().SAME_AS(), "SAME_AS" );
}
/**
* <p>Answer an iterator over all of the resources that are declared to be the same as
* this resource. Each element of the iterator will be an {@link OntResource}.</p>
* @return An iterator over the resources equivalent to this resource.
* @exception ProfileException If the {@link Profile#SAME_AS()} property is not supported in the current language profile.
*/
@Override
public ExtendedIterator<OntResource> listSameAs() {
return listAs( getProfile().SAME_AS(), "SAME_AS", OntResource.class );
}
/**
* <p>Answer true if this resource is the same as the given resource.</p>
* @param res A resource to test against
* @return True if the resources are declared the same via a <code>sameAs</code> statement.
*/
@Override
public boolean isSameAs( Resource res ) {
return hasPropertyValue( getProfile().SAME_AS(), "SAME_AS", res );
}
/**
* <p>Remove the statement that this resource is the same as the given resource. If this statement
* is not true of the current model, nothing happens.</p>
* @param res A resource that may be declared to be the sameAs this resource
*/
@Override
public void removeSameAs( Resource res ) {
removePropertyValue( getProfile().SAME_AS(), "SAME_AS", res );
}
// differentFrom
/**
* <p>Assert that the given resource and this resource are distinct. Any existing
* statements for <code>differentFrom</code> will be removed.</p>
* @param res The resource that is declared to be distinct from this resource
* @exception ProfileException If the {@link Profile#DIFFERENT_FROM()} property is not supported in the current language profile.
*/
@Override
public void setDifferentFrom( Resource res ) {
setPropertyValue( getProfile().DIFFERENT_FROM(), "DIFFERENT_FROM", res );
}
/**
* <p>Add a statement declaring that this resource is distinct from the given resource.</p>
* @param res A resource that declared to be distinct from this resource
* @exception ProfileException If the {@link Profile#DIFFERENT_FROM()} property is not supported in the current language profile.
*/
@Override
public void addDifferentFrom( Resource res ) {
addPropertyValue( getProfile().DIFFERENT_FROM(), "DIFFERENT_FROM", res );
}
/**
* <p>Answer a resource that is declared to be distinct from this resource. If there is
* more than one such resource, an arbitrary selection is made.</p>
* @return res An ont resource that declared to be different from this resource
* @exception ProfileException If the {@link Profile#DIFFERENT_FROM()} property is not supported in the current language profile.
*/
@Override
public OntResource getDifferentFrom() {
return objectAsResource( getProfile().DIFFERENT_FROM(), "DIFFERENT_FROM" );
}
/**
* <p>Answer an iterator over all of the resources that are declared to be different from
* this resource. Each element of the iterator will be an {@link OntResource}.</p>
* @return An iterator over the resources different from this resource.
* @exception ProfileException If the {@link Profile#DIFFERENT_FROM()} property is not supported in the current language profile.
*/
@Override
public ExtendedIterator<OntResource> listDifferentFrom() {
return listAs( getProfile().DIFFERENT_FROM(), "DIFFERENT_FROM", OntResource.class );
}
/**
* <p>Answer true if this resource is different from the given resource.</p>
* @param res A resource to test against
* @return True if the resources are declared to be distinct via a <code>differentFrom</code> statement.
*/
@Override
public boolean isDifferentFrom( Resource res ) {
return hasPropertyValue( getProfile().DIFFERENT_FROM(), "DIFFERENT_FROM", res );
}
/**
* <p>Remove the statement that this resource is different the given resource. If this statement
* is not true of the current model, nothing happens.</p>
* @param res A resource that may be declared to be differentFrom this resource
*/
@Override
public void removeDifferentFrom( Resource res ) {
removePropertyValue( getProfile().DIFFERENT_FROM(), "DIFFERENT_FROM", res );
}
// seeAlso
/**
* <p>Assert that the given resource provides additional information about the definition of this resource</p>
* @param res A resource that can provide additional information about this resource
* @exception ProfileException If the {@link Profile#SEE_ALSO()} property is not supported in the current language profile.
*/
@Override
public void setSeeAlso( Resource res ) {
setPropertyValue( getProfile().SEE_ALSO(), "SEE_ALSO", res );
}
/**
* <p>Add a resource that is declared to provided additional information about the definition of this resource</p>
* @param res A resource that provides extra information on this resource
* @exception ProfileException If the {@link Profile#SEE_ALSO()} property is not supported in the current language profile.
*/
@Override
public void addSeeAlso( Resource res ) {
addPropertyValue( getProfile().SEE_ALSO(), "SEE_ALSO", res );
}
/**
* <p>Answer a resource that provides additional information about this resource. If more than one such resource
* is defined, make an arbitrary choice.</p>
* @return res A resource that provides additional information about this resource
* @exception ProfileException If the {@link Profile#SEE_ALSO()} property is not supported in the current language profile.
*/
@Override
public Resource getSeeAlso() {
return objectAsResource( getProfile().SEE_ALSO(), "SEE_ALSO" );
}
/**
* <p>Answer an iterator over all of the resources that are declared to provide addition
* information about this resource.</p>
* @return An iterator over the resources providing additional definition on this resource.
* @exception ProfileException If the {@link Profile#SEE_ALSO()} property is not supported in the current language profile.
*/
@Override
public ExtendedIterator<RDFNode> listSeeAlso() {
checkProfile( getProfile().SEE_ALSO(), "SEE_ALSO" );
return WrappedIterator.create( listProperties( getProfile().SEE_ALSO() ) )
.mapWith( s -> asOntResource( s.getObject() ) );
}
/**
* <p>Answer true if this resource has the given resource as a source of additional information.</p>
* @param res A resource to test against
* @return True if the <code>res</code> provides more information on this resource.
*/
@Override
public boolean hasSeeAlso( Resource res ) {
return hasPropertyValue( getProfile().SEE_ALSO(), "SEE_ALSO", res );
}
/**
* <p>Remove the statement indicating the given resource as a source of additional information
* about this resource. If this statement
* is not true of the current model, nothing happens.</p>
* @param res A resource that may be declared to provide additional information about this resource
*/
@Override
public void removeSeeAlso( Resource res ) {
removePropertyValue( getProfile().SEE_ALSO(), "SEE_ALSO", res );
}
// is defined by
/**
* <p>Assert that the given resource provides a source of definitions about this resource. Any existing
* statements for <code>isDefinedBy</code> will be removed.</p>
* @param res The resource that is declared to be a definition of this resource.
* @exception ProfileException If the {@link Profile#IS_DEFINED_BY()} property is not supported in the current language profile.
*/
@Override
public void setIsDefinedBy( Resource res ) {
setPropertyValue( getProfile().IS_DEFINED_BY(), "IS_DEFINED_BY", res );
}
/**
* <p>Add a resource that is declared to provide a definition of this resource.</p>
* @param res A defining resource
* @exception ProfileException If the {@link Profile#IS_DEFINED_BY()} property is not supported in the current language profile.
*/
@Override
public void addIsDefinedBy( Resource res ) {
addPropertyValue( getProfile().IS_DEFINED_BY(), "IS_DEFINED_BY", res );
}
/**
* <p>Answer a resource that is declared to provide a definition of this resource. If there is
* more than one such resource, an arbitrary selection is made.</p>
* @return res An ont resource that is declared to provide a definition of this resource
* @exception ProfileException If the {@link Profile#IS_DEFINED_BY()} property is not supported in the current language profile.
*/
@Override
public Resource getIsDefinedBy() {
return objectAsResource( getProfile().IS_DEFINED_BY(), "IS_DEFINED_BY" );
}
/**
* <p>Answer an iterator over all of the resources that are declared to define
* this resource. </p>
* @return An iterator over the resources defining this resource.
* @exception ProfileException If the {@link Profile#IS_DEFINED_BY()} property is not supported in the current language profile.
*/
@Override
public ExtendedIterator<RDFNode> listIsDefinedBy() {
checkProfile( getProfile().IS_DEFINED_BY(), "IS_DEFINED_BY" );
return WrappedIterator.create( listProperties( getProfile().IS_DEFINED_BY() ) )
.mapWith( s -> asOntResource( s.getObject() ) );
}
/**
* <p>Answer true if this resource is defined by the given resource.</p>
* @param res A resource to test against
* @return True if <code>res</code> defines this resource.
*/
@Override
public boolean isDefinedBy( Resource res ) {
return hasPropertyValue( getProfile().IS_DEFINED_BY(), "IS_DEFINED_BY", res );
}
/**
* <p>Remove the statement that this resource is defined by the given resource. If this statement
* is not true of the current model, nothing happens.</p>
* @param res A resource that may be declared to define this resource
*/
@Override
public void removeDefinedBy( Resource res ) {
removePropertyValue( getProfile().IS_DEFINED_BY(), "IS_DEFINED_BY", res );
}
// version info
/**
* <p>Assert that the given string is the value of the version info for this resource. Any existing
* statements for <code>versionInfo</code> will be removed.</p>
* @param info The version information for this resource
* @exception ProfileException If the {@link Profile#VERSION_INFO()} property is not supported in the current language profile.
*/
@Override
public void setVersionInfo( String info ) {
checkProfile( getProfile().VERSION_INFO(), "VERSION_INFO" );
removeAll( getProfile().VERSION_INFO() );
addVersionInfo( info );
}
/**
* <p>Add the given version information to this resource.</p>
* @param info A version information string for this resource
* @exception ProfileException If the {@link Profile#VERSION_INFO()} property is not supported in the current language profile.
*/
@Override
public void addVersionInfo( String info ) {
checkProfile( getProfile().VERSION_INFO(), "VERSION_INFO" );
addProperty( getProfile().VERSION_INFO(), getModel().createLiteral( info ) );
}
/**
* <p>Answer the version information string for this object. If there is
* more than one such resource, an arbitrary selection is made.</p>
* @return A version info string
* @exception ProfileException If the {@link Profile#VERSION_INFO()} property is not supported in the current language profile.
*/
@Override
public String getVersionInfo() {
checkProfile( getProfile().VERSION_INFO(), "VERSION_INFO" );
try {
return getRequiredProperty( getProfile().VERSION_INFO() ).getString();
}
catch (PropertyNotFoundException ignore) {
return null;
}
}
/**
* <p>Answer an iterator over all of the version info strings for this resource.</p>
* @return An iterator over the version info strings for this resource.
* @exception ProfileException If the {@link Profile#VERSION_INFO()} property is not supported in the current language profile.
*/
@Override
public ExtendedIterator<String> listVersionInfo() {
checkProfile( getProfile().VERSION_INFO(), "VERSION_INFO" );
return WrappedIterator.create( listProperties( getProfile().VERSION_INFO() ) )
.mapWith( s -> s.getString() );
}
/**
* <p>Answer true if this resource has the given version information</p>
* @param info Version information to test for
* @return True if this resource has <code>info</code> as version information.
*/
@Override
public boolean hasVersionInfo( String info ) {
checkProfile( getProfile().VERSION_INFO(), "VERSION_INFO" );
return hasProperty( getProfile().VERSION_INFO(), info );
}
/**
* <p>Remove the statement that the given string provides version information about
* this resource. If this statement
* is not true of the current model, nothing happens.</p>
* @param info A version information string to be removed
*/
@Override
public void removeVersionInfo( String info ) {
checkProfile( getProfile().VERSION_INFO(), "VERSION_INFO" );
Literal infoAsLiteral = ResourceFactory.createPlainLiteral( info );
getModel().remove( this, getProfile().VERSION_INFO(), infoAsLiteral );
}
// label
/**
* <p>Assert that the given string is the value of the label for this resource. Any existing
* statements for <code>label</code> will be removed.</p>
* @param label The label for this resource
* @param lang The language attribute for this label (EN, FR, etc) or null if not specified.
* @exception ProfileException If the {@link Profile#LABEL()} property is not supported in the current language profile.
*/
@Override
public void setLabel( String label, String lang ) {
checkProfile( getProfile().LABEL(), "LABEL" );
removeAll( getProfile().LABEL() );
addLabel( label, lang );
}
/**
* <p>Add the given label to this resource.</p>
* @param label A label string for this resource
* @param lang The language attribute for this label (EN, FR, etc) or null if not specified.
* @exception ProfileException If the {@link Profile#LABEL()} property is not supported in the current language profile.
*/
@Override
public void addLabel( String label, String lang ) {
addLabel( getModel().createLiteral( label, lang ) );
}
/**
* <p>Add the given label to this resource.</p>
* @param label The literal label
* @exception ProfileException If the {@link Profile#LABEL()} property is not supported in the current language profile.
*/
@Override
public void addLabel( Literal label ) {
addPropertyValue( getProfile().LABEL(), "LABEL", label );
}
/**
* <p>Answer the label string for this object. If there is
* more than one such resource, an arbitrary selection is made.</p>
* @param lang The language attribute for the desired label (EN, FR, etc) or null for don't care. Will
* attempt to retreive the most specific label matching the given language</p>
* @return A label string matching the given language, or null if there is no matching label.
* @exception ProfileException If the {@link Profile#LABEL()} property is not supported in the current language profile.
*/
@Override
public String getLabel( String lang ) {
checkProfile( getProfile().LABEL(), "LABEL" );
if (lang == null || lang.length() == 0) {
// don't care which language version we get
try {
return getRequiredProperty( getProfile().LABEL() ).getString();
}
catch (PropertyNotFoundException ignore) {
return null;
}
}
else {
// search for the best match for the specified language
return selectLang( listProperties( getProfile().LABEL() ), lang );
}
}
/**
* <p>Answer an iterator over all of the label literals for this resource.</p>
* @param lang The language to restrict any label values to, or null to select all languages
* @return An iterator over RDF {@link Literal}'s.
* @exception ProfileException If the {@link Profile#LABEL()} property is not supported in the current language profile.
*/
@Override
public ExtendedIterator<RDFNode> listLabels( String lang ) {
checkProfile( getProfile().LABEL(), "LABEL" );
return WrappedIterator.create( listProperties( getProfile().LABEL() ) )
.filterKeep( new LangTagFilter( lang ) )
.mapWith( s -> s.getObject() );
}
/**
* <p>Answer true if this resource has the given label</p>
* @param label The label to test for
* @param lang The optional language tag, or null for don't care.
* @return True if this resource has <code>label</code> as a label.
*/
@Override
public boolean hasLabel( String label, String lang ) {
return hasLabel( getModel().createLiteral( label, lang ) );
}
/**
* <p>Answer true if this resource has the given label</p>
* @param label The label to test for
* @return True if this resource has <code>label</code> as a label.
*/
@Override
public boolean hasLabel( Literal label ) {
boolean found = false;
ExtendedIterator<RDFNode> i = listLabels( label.getLanguage() );
while (!found && i.hasNext()) {
found = label.equals( i.next() );
}
i.close();
return found;
}
/**
* <p>Remove the statement that the given string is a label for
* this resource. If this statement
* is not true of the current model, nothing happens.</p>
* @param label A label string to be removed
* @param lang A lang tag
*/
@Override
public void removeLabel( String label, String lang ) {
removeLabel( getModel().createLiteral( label, lang ) );
}
/**
* <p>Remove the statement that the given string is a label for
* this resource. If this statement
* is not true of the current model, nothing happens.</p>
* @param label A label literal to be removed
*/
@Override
public void removeLabel( Literal label ) {
removePropertyValue( getProfile().LABEL(), "LABEL", label );
}
// comment
/**
* <p>Assert that the given string is the comment on this resource. Any existing
* statements for <code>comment</code> will be removed.</p>
* @param comment The comment for this resource
* @param lang The language attribute for this comment (EN, FR, etc) or null if not specified.
* @exception ProfileException If the {@link Profile#COMMENT()} property is not supported in the current language profile.
*/
@Override
public void setComment( String comment, String lang ) {
checkProfile( getProfile().COMMENT(), "COMMENT" );
removeAll( getProfile().COMMENT() );
addComment( comment, lang );
}
/**
* <p>Add the given comment to this resource.</p>
* @param comment A comment string for this resource
* @param lang The language attribute for this comment (EN, FR, etc) or null if not specified.
* @exception ProfileException If the {@link Profile#COMMENT()} property is not supported in the current language profile.
*/
@Override
public void addComment( String comment, String lang ) {
addComment( getModel().createLiteral( comment, lang ) );
}
/**
* <p>Add the given comment to this resource.</p>
* @param comment The literal comment
* @exception ProfileException If the {@link Profile#COMMENT()} property is not supported in the current language profile.
*/
@Override
public void addComment( Literal comment ) {
checkProfile( getProfile().COMMENT(), "COMMENT" );
addProperty( getProfile().COMMENT(), comment );
}
/**
* <p>Answer the comment string for this object. If there is
* more than one such resource, an arbitrary selection is made.</p>
* @param lang The language attribute for the desired comment (EN, FR, etc) or null for don't care. Will
* attempt to retreive the most specific comment matching the given language</p>
* @return A comment string matching the given language, or null if there is no matching comment.
* @exception ProfileException If the {@link Profile#COMMENT()} property is not supported in the current language profile.
*/
@Override
public String getComment( String lang ) {
checkProfile( getProfile().COMMENT(), "COMMENT" );
if (lang == null) {
// don't care which language version we get
try {
return getRequiredProperty( getProfile().COMMENT() ).getString();
}
catch (PropertyNotFoundException ignore) {
// no comment :-)
return null;
}
}
else {
// search for the best match for the specified language
return selectLang( listProperties( getProfile().COMMENT() ), lang );
}
}
/**
* <p>Answer an iterator over all of the comment literals for this resource.</p>
* @return An iterator over RDF {@link Literal}'s.
* @exception ProfileException If the {@link Profile#COMMENT()} property is not supported in the current language profile.
*/
@Override
public ExtendedIterator<RDFNode> listComments( String lang ) {
checkProfile( getProfile().COMMENT(), "COMMENT" );
return WrappedIterator.create( listProperties( getProfile().COMMENT() ) )
.filterKeep( new LangTagFilter( lang ) )
.mapWith( s -> s.getObject() );
}
/**
* <p>Answer true if this resource has the given comment.</p>
* @param comment The comment to test for
* @param lang The optional language tag, or null for don't care.
* @return True if this resource has <code>comment</code> as a comment.
*/
@Override
public boolean hasComment( String comment, String lang ) {
return hasComment( getModel().createLiteral( comment, lang ) );
}
/**
* <p>Answer true if this resource has the given comment.</p>
* @param comment The comment to test for
* @return True if this resource has <code>comment</code> as a comment.
*/
@Override
public boolean hasComment( Literal comment ) {
boolean found = false;
ExtendedIterator<RDFNode> i = listComments( comment.getLanguage() );
while (!found && i.hasNext()) {
found = comment.equals( i.next() );
}
i.close();
return found;
}
/**
* <p>Remove the statement that the given string is a comment on
* this resource. If this statement
* is not true of the current model, nothing happens.</p>
* @param comment A comment string to be removed
* @param lang A lang tag
*/
@Override
public void removeComment( String comment, String lang ) {
removeComment( getModel().createLiteral( comment, lang ) );
}
/**
* <p>Remove the statement that the given string is a comment on
* this resource. If this statement
* is not true of the current model, nothing happens.</p>
* @param comment A comment literal to be removed
*/
@Override
public void removeComment( Literal comment ) {
removePropertyValue( getProfile().COMMENT(), "COMMENT", comment );
}
// rdf:type
/**
* <p>Set the RDF type (i.e. the class) for this resource, replacing any
* existing <code>rdf:type</code> property. Any existing statements for the RDF type
* will first be removed.</p>
*
* @param cls The RDF resource denoting the new value for the <code>rdf:type</code> property,
* which will replace any existing type property.
*/
@Override
public void setRDFType( Resource cls ) {
setPropertyValue( RDF.type, "rdf:type", cls );
}
/**
* <p>Add the given class as one of the <code>rdf:type</code>'s for this resource.</p>
*
* @param cls An RDF resource denoting a new value for the <code>rdf:type</code> property.
*/
@Override
public void addRDFType( Resource cls ) {
addPropertyValue( RDF.type, "rdf:type", cls );
}
/**
* <p>
* Answer the <code>rdf:type</code> (ie the class) of this resource. If there
* is more than one type for this resource, the return value will be one of
* the values, but it is not specified which one (nor that it will consistently
* be the same one each time). Equivalent to <code>getRDFType( false )</code>.
* </p>
*
* @return A resource that is the rdf:type for this resource, or one of them if
* more than one is defined.
*/
@Override
public Resource getRDFType() {
return getRDFType( false );
}
/**
* <p>
* Answer the <code>rdf:type</code> (ie the class) of this resource. If there
* is more than one type for this resource, the return value will be one of
* the values, but it is not specified which one (nor that it will consistently
* be the same one each time).
* </p>
*
* @param direct If true, only consider the direct types of this resource, and not
* the super-classes of the type(s).
* @return A resource that is the rdf:type for this resource, or one of them if
* more than one is defined.
*/
@Override
public Resource getRDFType( boolean direct ) {
ExtendedIterator<Resource> i = null;
try {
i = listRDFTypes( direct );
return i.hasNext() ? i.next(): null;
}
finally {
if ( i != null ) i.close();
}
}
/**
* <p>
* Answer an iterator over the RDF classes to which this resource belongs.
* </p>
*
* @param direct If true, only answer those resources that are direct types
* of this resource, not the super-classes of the class etc.
* @return An iterator over the set of this resource's classes, each of which
* will be a {@link Resource}.
*/
@Override
public ExtendedIterator<Resource> listRDFTypes( boolean direct ) {
ExtendedIterator<Resource> i = listDirectPropertyValues( RDF.type, "rdf:type", Resource.class, getProfile().SUB_CLASS_OF(), direct, false );
// we only want each result once
return i.filterKeep( new UniqueFilter<Resource>());
}
/**
* <p>
* Answer true if this resource is a member of the class denoted by the
* given URI.</p>
*
* @param uri Denotes the URI of a class to which this value may belong
* @return True if this resource has the given class as one of its <code>rdf:type</code>'s.
*/
@Override
public boolean hasRDFType( String uri ) {
return hasRDFType( getModel().getResource( uri ) );
}
/**
* <p>
* Answer true if this resource is a member of the class denoted by the
* given class resource. Includes all available types, so is equivalent to
* <code><pre>
* hasRDF( ontClass, false );
* </pre></code>
* </p>
*
* @param ontClass Denotes a class to which this value may belong
* @return True if this resource has the given class as one of its <code>rdf:type</code>'s.
*/
@Override
public boolean hasRDFType( Resource ontClass ) {
return hasRDFType( ontClass, "unknown", false );
}
/**
* <p>
* Answer true if this resource is a member of the class denoted by the
* given class resource.
* </p>
*
* @param ontClass Denotes a class to which this value may belong
* @param direct If true, only consider the direct types of this resource, ignoring
* the super-classes of the stated types.
* @return True if this resource has the given class as one of its <code>rdf:type</code>'s.
*/
@Override
public boolean hasRDFType( Resource ontClass, boolean direct ) {
return hasRDFType( ontClass, "unknown", direct );
}
protected boolean hasRDFType( Resource ontClass, String name, boolean direct ) {
checkProfile( ontClass, name );
if (!direct) {
// just an ordinary query - we can answer this directly (more efficient)
return hasPropertyValue( RDF.type, "rdf:type", ontClass );
}
else {
// need the direct version - not so efficient
ExtendedIterator<Resource> i = null;
try {
i = listRDFTypes( true );
while (i.hasNext()) {
if (ontClass.equals( i.next() )) {
return true;
}
}
return false;
}
finally {
if ( i != null ) i.close();
}
}
}
/**
* <p>Remove the statement that this resource is of the given RDF type. If this statement
* is not true of the current model, nothing happens.</p>
* @param cls A resource denoting a class that that is to be removed from the classes of this resource
*/
@Override
public void removeRDFType( Resource cls ) {
removePropertyValue( RDF.type, "rdf:type", cls );
}
// utility methods
/**
* <p>Answer the cardinality of the given property on this resource. The cardinality
* is the number of distinct values there are for the property.</p>
* @param p A property
* @return The cardinality for the property <code>p</code> on this resource, as an
* integer greater than or equal to zero.
*/
@Override
public int getCardinality( Property p ) {
int n = 0;
for (Iterator<RDFNode> i =listPropertyValues( p ).filterKeep( new UniqueFilter<RDFNode>()); i.hasNext(); n++) {
i.next();
}
return n;
}
/**
* <p>
* Set the value of the given property of this ontology resource to the given
* value, encoded as an RDFNode. Maintains the invariant that there is
* at most one value of the property for a given resource, so existing
* property values are first removed. To add multiple properties, use
* {@link #addProperty( Property, RDFNode ) addProperty}.
* </p>
*
* @param property The property to update
* @param value The new value of the property as an RDFNode, or null to
* effectively remove this property.
*/
@Override
public void setPropertyValue( Property property, RDFNode value ) {
// if there is an existing property, remove it
removeAll( property );
// now set the new value
if (value != null) {
addProperty( property, value );
}
}
/**
* <p>Answer the value of a given RDF property for this ontology resource, or null
* if it doesn't have one. The value is returned as an RDFNode, from which
* the concrete data value can be extracted for literals. If the value is
* a resource, it will present the {@link OntResource} facet.
* If there is more than one RDF
* statement with the given property for the current value, it is not defined
* which of the values will be returned.</p>
*
* @param property An RDF property
* @return An RDFNode whose value is the value, or one of the values, of the
* given property. If the property is not defined the method returns null.
*/
@Override
public RDFNode getPropertyValue( Property property ) {
Statement s = getProperty( property );
if (s == null) {
return null;
}
else {
return asOntResource( s.getObject() );
}
}
/**
* <p>Answer an iterator over the set of all values for a given RDF property. Each
* value in the iterator will be an RDFNode, representing the value (object) of
* each statement in the underlying model.</p>
*
* @param property The property whose values are sought
* @return An Iterator over the values of the property
*/
@Override
public NodeIterator listPropertyValues( Property property ) {
return new NodeIteratorImpl( listProperties( property ).mapWith( s -> asOntResource( s.getObject() ) ), null );
}
/**
* <p>Removes this resource from the ontology by deleting any statements that refer to it,
* as either statement-subject or statement-object.
* If this resource is a property, this method will <strong>not</strong> remove statements
* whose predicate is this property.</p>
* <p><strong>Caveat:</strong> Jena RDF models contain statements, not resources <em>per se</em>,
* so this method simulates removal of an object by removing all of the statements that have
* this resource as subject or object, with one exception. If the resource is referenced
* in an RDF List, i.e. as the object of an <code>rdf:first</code> statement in a list cell,
* this reference is <strong>not</strong> removed. Removing an arbitrary <code>rdf:first</code>
* statement from the midst of a list, without doing other work to repair the list, would
* leave an ill-formed list in the model. Therefore, if this resource is known to appear
* in a list somewhere in the model, it should be separately deleted from that list before
* calling this remove method.
* </p>
*/
@Override
public void remove() {
Set<Statement> stmts = new HashSet<>();
List<Resource> lists = new ArrayList<>();
List<Statement> skip = new ArrayList<>();
Property first = getProfile().FIRST();
// collect statements mentioning this object
for (StmtIterator i = listProperties(); i.hasNext(); ) {
stmts.add( i.next() );
}
for (StmtIterator i = getModel().listStatements( null, null, this ); i.hasNext(); ) {
stmts.add( i.next() );
}
// check for lists
for ( Statement s : stmts )
{
if ( s.getPredicate().equals( first ) && s.getObject().equals( this ) )
{
// _this_ is referenced from inside a list
// we don't delete this reference, since it would make the list ill-formed
log.debug( toString() + " is referened from an RDFList, so will not be fully removed" );
skip.add( s );
}
else if ( s.getObject() instanceof Resource )
{
// check for list-valued properties
Resource obj = s.getResource();
if ( obj.canAs( RDFList.class ) )
{
// this value is a list, so we will want to remove all of the elements
lists.add( obj );
}
}
}
// add in the contents of the lists to the statements to be removed
for ( Resource r : lists )
{
stmts.addAll( ( (RDFListImpl) r.as( RDFList.class ) ).collectStatements() );
}
// skip the contents of the skip list
stmts.removeAll( skip );
// and then remove the remainder
for ( Statement stmt : stmts )
{
stmt.remove();
}
}
/**
* <p>Remove the specific RDF property-value pair from this DAML resource.</p>
*
* @param property The property to be removed
* @param value The specific value of the property to be removed
*/
@Override
public void removeProperty( Property property, RDFNode value ) {
getModel().remove( this, property, value );
}
/**
* <p>Answer a view of this resource as an annotation property</p>
* @return This resource, but viewed as an AnnotationProperty
* @exception ConversionException if the resource cannot be converted to an annotation property
*/
@Override
public AnnotationProperty asAnnotationProperty() {
return as( AnnotationProperty.class );
}
/**
* <p>Answer a view of this resource as a property</p>
* @return This resource, but viewed as an OntProperty
* @exception ConversionException if the resource cannot be converted to a property
*/
@Override
public OntProperty asProperty() {
return as( OntProperty.class );
}
/**
* <p>Answer a view of this resource as an object property</p>
* @return This resource, but viewed as an ObjectProperty
* @exception ConversionException if the resource cannot be converted to an object property
*/
@Override
public ObjectProperty asObjectProperty() {
return as( ObjectProperty.class );
}
/**
* <p>Answer a view of this resource as a datatype property</p>
* @return This resource, but viewed as a DatatypeProperty
* @exception ConversionException if the resource cannot be converted to a datatype property
*/
@Override
public DatatypeProperty asDatatypeProperty() {
return as( DatatypeProperty.class );
}
/**
* <p>Answer a view of this resource as an individual</p>
* @return This resource, but viewed as an Individual
* @exception ConversionException if the resource cannot be converted to an individual
*/
@Override
public Individual asIndividual() {
return as( Individual.class );
}
/**
* <p>Answer a view of this resource as a class</p>
* @return This resource, but viewed as an OntClass
* @exception ConversionException if the resource cannot be converted to a class
*/
@Override
public OntClass asClass() {
return as( OntClass.class );
}
/**
* <p>Answer a view of this resource as an ontology description node</p>
* @return This resource, but viewed as an Ontology
* @exception ConversionException if the resource cannot be converted to an ontology description node
*/
@Override
public Ontology asOntology() {
return as( Ontology.class );
}
/**
* <p>Answer a view of this resource as an 'all different' declaration</p>
* @return This resource, but viewed as an AllDifferent node
* @exception ConversionException if the resource cannot be converted to an all different declaration
*/
@Override
public AllDifferent asAllDifferent() {
return as( AllDifferent.class );
}
/**
* <p>Answer a view of this resource as a data range</p>
* @return This resource, but viewed as a DataRange
* @exception ConversionException if the resource cannot be converted to a data range
*/
@Override
public DataRange asDataRange() {
return as( DataRange.class );
}
// Conversion test methods
/**
* <p>Answer true if this resource can be viewed as an annotation property</p>
* @return True if this resource can be viewed as an AnnotationProperty
*/
@Override
public boolean isAnnotationProperty() {
return getProfile().ANNOTATION_PROPERTY() != null && canAs( AnnotationProperty.class );
}
/**
* <p>Answer true if this resource can be viewed as a property</p>
* @return True if this resource can be viewed as an OntProperty
*/
@Override
public boolean isProperty() {
return canAs( OntProperty.class );
}
/**
* <p>Answer true if this resource can be viewed as an object property</p>
* @return True if this resource can be viewed as an ObjectProperty
*/
@Override
public boolean isObjectProperty() {
return getProfile().OBJECT_PROPERTY() != null && canAs( ObjectProperty.class );
}
/**
* <p>Answer true if this resource can be viewed as a datatype property</p>
* @return True if this resource can be viewed as a DatatypeProperty
*/
@Override
public boolean isDatatypeProperty() {
return getProfile().DATATYPE_PROPERTY() != null && canAs( DatatypeProperty.class );
}
/**
* <p>Answer true if this resource can be viewed as an individual</p>
* @return True if this resource can be viewed as an Individual
*/
@Override
public boolean isIndividual() {
OntModel m = (getModel() instanceof OntModel) ? (OntModel) getModel() : null;
if ( m == null )
return false ;
// can we use the reasoner's native abilities to do the instance test?
boolean useInf = false;
useInf = m.getProfile().THING() != null &&
m.getReasoner() != null &&
m.getReasoner().supportsProperty( ReasonerVocabulary.individualAsThingP );
StmtIterator i = null, j = null;
try {
if (!useInf) {
// either not using the OWL reasoner, or not using OWL
// look for an rdf:type of this resource that is a class
for (i = listProperties( RDF.type ); i.hasNext(); ) {
Resource rType = i.nextStatement().getResource();
if (rType.equals( m.getProfile().THING() )) {
// the resource has rdf:type owl:Thing (or equivalent)
return true;
}
// if the type itself is OWL.Class or similar, we should ignore it ... this may
// arise in cases where the user has materialised results of inference and is then
// accessing them from a plain model
// JENA-3: we also ignore if the type is rdfs:Resource or similar, since it's not informative
if (rType.equals( getProfile().CLASS() ) ||
rType.equals( RDFS.Resource ) ||
rType.equals( RDF.Property ) ||
rType.equals( RDFS.Datatype ) ||
rType.equals( RDF.List ))
{
continue;
}
// otherwise, we check to see if the given type is known to be a class
for (j = rType.listProperties( RDF.type ); j.hasNext(); ) {
if (j.nextStatement().getResource().equals( getProfile().CLASS() )) {
// we have found an rdf:type of the subject that is an owl, rdfs or daml Class
// therefore this is an individual
return true;
}
}
}
// apparently not an instance
return false;
}
else {
// using the rule reasoner on an OWL graph, so we can determine
// individuals as those things that have rdf:type owl:Thing
return hasProperty( RDF.type, getProfile().THING() );
}
}
finally {
if (i != null) { i.close(); }
if (j != null) { j.close(); }
}
}
/**
* <p>Answer true if this resource can be viewed as a class</p>
* @return True if this resource can be viewed as an OntClass
*/
@Override
public boolean isClass() {
return canAs( OntClass.class );
}
/**
* <p>Answer true if this resource can be viewed as an ontology description node</p>
* @return True if this resource can be viewed as an Ontology
*/
@Override
public boolean isOntology() {
return getProfile().ONTOLOGY() != null && canAs( Ontology.class );
}
/**
* <p>Answer true if this resource can be viewed as a data range</p>
* @return True if this resource can be viewed as a DataRange
*/
@Override
public boolean isDataRange() {
return getProfile().DATARANGE() != null && canAs( DataRange.class );
}
/**
* <p>Answer true if this resource can be viewed as an 'all different' declaration</p>
* @return True if this resource can be viewed as an AllDifferent node
*/
@Override
public boolean isAllDifferent() {
return getProfile().ALL_DIFFERENT() != null && canAs( AllDifferent.class );
}
// Internal implementation methods
//////////////////////////////////
/** Answer true if the node has the given type in the graph */
protected static boolean hasType( Node n, EnhGraph g, Resource type ) {
// TODO this method doesn't seem to be called anywhere.
boolean hasType = false;
ClosableIterator<Triple> i = g.asGraph().find( n, RDF.type.asNode(), type.asNode() );
hasType = i.hasNext();
i.close();
return hasType;
}
/**
* Throw an exception if a term is not in the profile
* @param term The term being checked
* @param name The name of the term
* @exception ProfileException if term is null (indicating it is not in the profile)
**/
protected void checkProfile( Object term, String name ) {
if (term == null) {
throw new ProfileException( name, getProfile() );
}
}
/**
* <p>Answer the literal with the language tag that best matches the required language</p>
* @param stmts A StmtIterator over the candidates
* @param lang The language we're searching for, assumed non-null.
* @return The literal value that best matches the given language tag, or null if there are no matches
*/
protected String selectLang( StmtIterator stmts, String lang ) {
String found = null;
while (stmts.hasNext()) {
RDFNode n = stmts.nextStatement().getObject();
if (n instanceof Literal) {
Literal l = (Literal) n;
String lLang = l.getLanguage();
// is this a better match?
if (lang.equalsIgnoreCase( lLang )) {
// exact match
found = l.getString();
break;
}
else if (lLang != null && lLang.length() > 1 && lang.equalsIgnoreCase( lLang.substring( 0, 2 ) )) {
// partial match - want EN, found EN-GB
// keep searching in case there's a better
found = l.getString();
}
else if (found == null && lLang == null) {
// found a string with no (i.e. default) language - keep this unless we've got something better
found = l.getString();
}
}
}
stmts.close();
return found;
}
/** Answer true if the desired lang tag matches the target lang tag */
protected boolean langTagMatch( String desired, String target ) {
return (desired == null) ||
(desired.equalsIgnoreCase( target )) ||
(target.length() > desired.length() && desired.equalsIgnoreCase( target.substring( desired.length() ) ));
}
/** Answer the object of a statement with the given property, .as() the given class */
protected <T extends RDFNode> T objectAs( Property p, String name, Class<T> asClass ) {
checkProfile( p, name );
try {
return getRequiredProperty( p ).getObject().as( asClass );
}
catch (PropertyNotFoundException e) {
return null;
}
}
/** Answer the object of a statement with the given property, .as() an OntResource */
protected OntResource objectAsResource( Property p, String name ) {
return objectAs( p, name, OntResource.class );
}
/** Answer the object of a statement with the given property, .as() an OntProperty */
protected OntProperty objectAsProperty( Property p, String name ) {
return objectAs( p, name, OntProperty.class );
}
/** Answer the int value of a statement with the given property */
protected int objectAsInt( Property p, String name ) {
checkProfile( p, name );
return getRequiredProperty( p ).getInt();
}
/** Answer an iterator for the given property, whose values are .as() some class */
protected <T extends RDFNode> ExtendedIterator<T> listAs( Property p, String name, Class<T> cls ) {
checkProfile( p, name );
return WrappedIterator.create( listProperties( p ) ).mapWith( s -> s.getObject().as( cls ) );
}
/** Add the property value, checking that it is supported in the profile */
protected void addPropertyValue( Property p, String name, RDFNode value ) {
checkProfile( p, name );
addProperty( p, value );
}
/** Set the property value, checking that it is supported in the profile */
protected void setPropertyValue( Property p, String name, RDFNode value ) {
checkProfile( p, name );
removeAll( p );
addProperty( p, value );
}
/** Answer true if the given property is defined in the profile, and has the given value */
protected boolean hasPropertyValue( Property p, String name, RDFNode value ) {
checkProfile( p, name );
return hasProperty( p, value );
}
/** Add the given value to a list which is the value of the given property */
protected void addListPropertyValue( Property p, String name, RDFNode value ) {
checkProfile( p, name );
// get the list value
if (hasProperty( p )) {
RDFNode cur = getRequiredProperty( p ).getObject();
if (!cur.canAs( RDFList.class )) {
throw new OntologyException( "Tried to add a value to a list-valued property " + p +
" but the current value is not a list: " + cur );
}
RDFList values = cur.as( RDFList.class );
// now add our value to the list
if (!values.contains( value )){
RDFList newValues = values.with( value );
// if the previous values was nil, the return value will be a new list
if (newValues != values) {
removeAll( p );
addProperty( p, newValues );
}
}
}
else {
// create a new list to hold the only value we know so far
addProperty( p, ((OntModel) getModel()).createList( new RDFNode[] {value} ) );
}
}
/** Convert this resource to the facet denoted by cls, by adding rdf:type type if necessary */
protected <T extends RDFNode> T convertToType( Resource type, String name, Class<T> cls ) {
checkProfile( type, name );
if (canAs( cls )) {
// don't need to update the model, we already can do the given facet
return as( cls );
}
// we're told that adding this rdf:type will make the as() possible - let's see
addProperty( RDF.type, type );
return as( cls );
}
/**
* <p>Return an iterator of values, respecting the 'direct' modifier</p>
* @param p The property whose values are required
* @param name The short name of the property (for generating error messages)
* @param cls Class object denoting the facet to map the returned values to
* @param orderRel If direct, and we are not using an inference engine, this is the property
* to use to define the maximal lower elements of the partial order
* @param direct If true, only return the direct (adjacent) values
* @param inverse If true, use the inverse of p rather than p
* @return An iterator of nodes that are in relation p to this resource (possibly inverted), which
* have been mapped to the facet denoted by <code>cls</code>.
*/
protected <T extends Resource> ExtendedIterator<T> listDirectPropertyValues( Property p, String name, Class<T> cls, Property orderRel, boolean direct, boolean inverse ) {
Iterator<T> i = null;
checkProfile( p, name );
Property sc = p;
// check for requesting direct versions of these properties
if (direct) {
sc = getModel().getProperty( ReasonerRegistry.makeDirect( sc.asNode() ).getURI() );
}
// determine the subject and object pairs for the list statements calls
Resource subject = inverse ? null : this;
Resource object = inverse ? this : null;
Function<Statement, T> mapper = inverse ? s -> s.getSubject().as( cls ) : s -> s.getObject().as( cls );
// are we working on an inference graph?
OntModel m = (OntModel) getGraph();
InfGraph ig = null;
if (m.getGraph() instanceof InfGraph) {
ig = (InfGraph) m.getGraph();
}
// can we go direct to the graph?
if (!direct || ((ig != null) && ig.getReasoner().supportsProperty( sc ))) {
// either not direct, or the direct sc property is supported
// ensure we have an extended iterator of statements this rdfs:subClassOf _x
// NB we only want the subjects or objects of the statements
i = getModel().listStatements( subject, sc, object ).mapWith( mapper );
}
else {
i = computeDirectValues( p, orderRel, inverse, subject, object, mapper );
}
return WrappedIterator.create(i).filterKeep( new UniqueFilter<T>());
}
/**
* <p>In the absence of a reasoner that can compute direct (adjacent) property values,
* we must perform the calculation of the direct values computationally here.</p>
* @param p
* @param orderRel
* @param inverse
* @param subject
* @param object
* @param mapper
* @return
*/
@SuppressWarnings("unchecked")
private <T extends Resource> Iterator<T> computeDirectValues( Property p, Property orderRel, boolean inverse, Resource subject, Resource object, Function<Statement, T> mapper ) {
// graph does not support direct directly
ExtendedIterator<T> j = getModel().listStatements( subject, p, object )
.mapWith( mapper );
// collect a list of the candidates
List<T> s = new ArrayList<>();
for( ; j.hasNext(); ) {
s.add( j.next() );
}
// we need to keep this node out of the iterator for now, else it will spoil the maximal
// generator compression (since all the (e.g.) sub-classes will be sub-classes of this node
// and so will be excluded from the maximal lower elements calculation)
ResourceUtils.removeEquiv( s, orderRel, this );
boolean withheld = s.remove( this );
// we now compress the list by reducing all equivalent values to a single representative
// first partition the list by equivalence under orderRel
List<List<T>> partition = ResourceUtils.partition( s, orderRel );
Map<Resource, List<T>> equivSets = new HashMap<>();
// then reduce each part of the partition to a singleton, but remember the others
s.clear();
for ( List<T> part : partition )
{
// if this is a singleton we just add it to the compressed candidates
if ( part.size() == 1 )
{
s.add( part.get( 0 ) );
}
else
{
// we select a single representative
T r = part.remove( 0 );
// remember the other equivalent values
equivSets.put( r, part );
s.add( r );
}
}
// now s1 contains a reduced set of nodes, in which any fully-connected sub-graph under
// orderRel has been reduced to a single representative
// generate the short list as the maximal bound under the given partial order
s = ResourceUtils.maximalLowerElements( s, orderRel, inverse );
// create a list of these values lower elements, plus their equivalents (if any)
List<T> s2 = new ArrayList<>();
for ( T r : s )
{
s2.add( r );
if ( equivSets.containsKey( r ) )
{
s2.addAll( equivSets.get( r ) );
}
}
// put myself back if needed
if (withheld) {
s2.add( (T) this );
}
return s2.iterator();
}
/** Remove a specified property-value pair, if it exists */
protected void removePropertyValue( Property prop, String name, RDFNode value ) {
checkProfile( prop, name );
getModel().remove( this, prop, value );
}
/** Answer the given node presenting the OntResource facet if it can */
private static RDFNode asOntResource( RDFNode n ) {
return n.isResource() ? n.as( OntResource.class ) : n;
}
//==============================================================================
// Inner class definitions
//==============================================================================
/** Filter for matching language tags on the objects of statements */
protected class LangTagFilter implements Predicate<Statement>
{
protected String m_lang;
public LangTagFilter( String lang ) { m_lang = lang; }
@Override
public boolean test( Statement x ) {
RDFNode o = x.getObject();
return o.isLiteral() && langTagMatch( m_lang, o.asLiteral().getLanguage() );
}
}
}